Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 45f7111fb9 | |||
| f2e23a8062 | |||
| 36a3631f5e | |||
| a758d19a9f | |||
| aac3e53a30 | |||
| 85d87ee210 | |||
| 2f3ee80ed4 | |||
| 3632bea54e | |||
| 4a0521751a | |||
| 7d6266c241 | |||
| 1d69b9ffea | |||
| 72674cba10 | |||
| 495a8e2d1e | |||
| 95ae221d60 | |||
| 857f746da1 | |||
| 0e2e3052b1 | |||
| 167c04cc3f | |||
| 043f51479a | |||
| 921282df81 | |||
| b3c641501c | |||
| 3608e7601f | |||
| a75cfa1056 | |||
| f08c4102fd | |||
| 9bfd7c7d9a | |||
| a359b4fbdb | |||
| effb6df495 | |||
| 6e6c963663 | |||
| 23f5c7b578 | |||
| 02b223266d | |||
| 745a8c99e1 | |||
| 5532038fd8 | |||
| 3be31c5240 | |||
| 11ab59e109 | |||
| d5bbf10041 | |||
| ba4f9581a4 | |||
| 52eabab633 | |||
| 600949b784 | |||
| dcca48097d | |||
| 6e20c32a14 |
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.17](https://github.com/copenomics/cooparser/compare/cooparser-ts@1.0.17-alpha.2...cooparser-ts@1.0.17) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package cooparser-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.16](https://github.com/copenomics/cooparser/compare/cooparser-ts@1.0.16-alpha.0...cooparser-ts@1.0.16) (2024-07-15)
|
||||
|
||||
**Note:** Version bump only for package cooparser-ts
|
||||
|
||||
Generated
+2
-3
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "pkg-placeholder",
|
||||
"version": "1.0.16",
|
||||
"version": "1.0.17",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pkg-placeholder",
|
||||
"version": "1.0.16",
|
||||
"version": "1.0.17",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^2.16.0",
|
||||
@@ -16,7 +16,6 @@
|
||||
"bumpp": "^9.4.0",
|
||||
"eslint": "^8.57.0",
|
||||
"esno": "^4.7.0",
|
||||
"lint-staged": "^15.2.2",
|
||||
"pnpm": "^8.15.7",
|
||||
"rimraf": "^5.0.5",
|
||||
"simple-git-hooks": "^2.11.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cooparser-ts",
|
||||
"type": "module",
|
||||
"version": "1.0.16",
|
||||
"version": "1.0.17",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.0.6",
|
||||
"description": "",
|
||||
@@ -41,6 +41,7 @@
|
||||
"deploy-production": "git checkout production && git merge testnet && git push origin production && git checkout main",
|
||||
"build": "unbuild",
|
||||
"dev": "concurrently -n 'PARSER' -c 'bgBlue.white' \"nodemon --watch src --ext ts,js,env --exec 'esno' src/index.ts\"",
|
||||
"dev:test": "NODE_ENV=test concurrently -n 'PARSER' -c 'bgBlue.white' \"nodemon --watch src --ext ts,js,env --exec 'esno' src/index.ts\"",
|
||||
"lint": "eslint .",
|
||||
"prepublishOnly": "nr build",
|
||||
"release": "bumpp && npm publish",
|
||||
@@ -72,7 +73,6 @@
|
||||
"concurrently": "^8.2.2",
|
||||
"eslint": "^8.57.0",
|
||||
"esno": "^4.7.0",
|
||||
"lint-staged": "^15.2.2",
|
||||
"nodemon": "^3.1.4",
|
||||
"pnpm": "^8.15.7",
|
||||
"rimraf": "^5.0.5",
|
||||
@@ -82,11 +82,5 @@
|
||||
"unbuild": "^2.0.0",
|
||||
"vite": "^5.2.10",
|
||||
"vitest": "^1.5.2"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "pnpm lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*": "eslint --fix"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,13 @@ export class Database {
|
||||
private sync: Collection | undefined
|
||||
|
||||
constructor() {
|
||||
console.log('mongo2: ', mongoUri)
|
||||
this.client = new MongoClient(mongoUri)
|
||||
}
|
||||
|
||||
async connect() {
|
||||
await this.client.connect()
|
||||
this.db = this.client.db('cooperative')
|
||||
this.db = this.client.db()
|
||||
this.actions = this.db.collection('actions')
|
||||
this.deltas = this.db.collection('deltas')
|
||||
this.sync = this.db.collection('sync')
|
||||
|
||||
@@ -10,10 +10,11 @@ function getEnvVar(key: string): string {
|
||||
|
||||
return envVar
|
||||
}
|
||||
|
||||
export const node_env = getEnvVar('NODE_ENV')
|
||||
console.log('ENV: ', node_env)
|
||||
export const eosioApi = getEnvVar('API')
|
||||
export const shipApi = getEnvVar('SHIP')
|
||||
export const mongoUri = getEnvVar('MONGO_EXPLORER_URI')
|
||||
export const mongoUri = `${getEnvVar('MONGO_EXPLORER_URI')}${node_env === 'test' ? '-test' : ''}`
|
||||
export const startBlock = getEnvVar('START_BLOCK')
|
||||
export const finishBlock = getEnvVar('FINISH_BLOCK')
|
||||
export const redisPort = getEnvVar('REDIS_PORT')
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
"extends": ["airbnb-base", "plugin:jest/recommended", "plugin:security/recommended", "plugin:prettier/recommended"],
|
||||
"plugins": ["jest", "security", "prettier", "import"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any":"off",
|
||||
"no-console": "error",
|
||||
"func-names": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"consistent-return": "off",
|
||||
"jest/expect-expect": "off",
|
||||
"security/detect-object-injection": "off",
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
{
|
||||
"js": "never",
|
||||
"jsx": "never",
|
||||
"ts": "never",
|
||||
"tsx": "never"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"jest": true,
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"plugins": ["@typescript-eslint", "prettier"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "error",
|
||||
"func-names": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"consistent-return": "off",
|
||||
"jest/expect-expect": "off",
|
||||
"security/detect-object-injection": "off",
|
||||
"@typescript-eslint/no-unused-vars": "warn"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
# . "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
yarn install
|
||||
# yarn install
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
# . "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
git status
|
||||
# git status
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
# . "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
yarn lint-staged
|
||||
# yarn lint-staged
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"CodeGPT.apiKey": "OpenAI",
|
||||
"cSpell.words": [
|
||||
"subcode"
|
||||
]
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.7.28](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.28-alpha.0...coopback@1.7.28) (2024-07-26)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.27](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.27-alpha.5...coopback@1.7.27) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.26](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.26-alpha.0...coopback@1.7.26) (2024-07-15)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
Generated
+4
-5
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "create-nodejs-express-app",
|
||||
"version": "1.7.26",
|
||||
"version": "1.7.29-alpha.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "create-nodejs-express-app",
|
||||
"version": "1.7.26",
|
||||
"version": "1.7.29-alpha.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@a2seven/yoo-checkout": "^1.1.4",
|
||||
@@ -32,7 +32,7 @@
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"moment": "^2.24.0",
|
||||
"mongoose": "^5.7.7",
|
||||
"mongoose": "^8.5.2",
|
||||
"morgan": "^1.9.1",
|
||||
"nodemailer": "^6.3.1",
|
||||
"nunjucks": "^3.2.4",
|
||||
@@ -54,14 +54,13 @@
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^20.11.26",
|
||||
"coveralls": "^3.0.7",
|
||||
"eslint": "^7.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb-base": "^14.0.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jest": "^24.0.1",
|
||||
"eslint-plugin-prettier": "^3.1.1",
|
||||
"eslint-plugin-security": "^1.4.0",
|
||||
"lint-staged": "^11.0.0",
|
||||
"node-mocks-http": "^1.8.0",
|
||||
"nodemon": "^2.0.0",
|
||||
"prettier": "^2.0.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "coopback",
|
||||
"version": "1.7.26",
|
||||
"version": "1.7.29-alpha.0",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"bin": "bin/createNodejsApp.js",
|
||||
@@ -8,6 +8,7 @@
|
||||
"repository": "https://github.com/hagopj13/node-express-boilerplate.git",
|
||||
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
|
||||
"license": "MIT",
|
||||
"types": "src/types/index.ts",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
@@ -16,7 +17,7 @@
|
||||
"deploy-production": "git checkout production && git merge testnet && git push origin production && git checkout main",
|
||||
"start": "ts-node src/index.ts",
|
||||
"dev": "concurrently -n 'CONTROLLER' -c 'bgCyan.black' \"nodemon --watch src --ext ts,js,env --exec 'ts-node' src/index.ts\"",
|
||||
"test": "jest -i --colors --verbose --detectOpenHandles",
|
||||
"test": "jest -i --colors --testTimeout=120000",
|
||||
"test:watch": "jest -i --watchAll",
|
||||
"coverage": "jest -i --coverage",
|
||||
"coverage:coveralls": "jest -i --coverage --coverageReporters=text-lcov | coveralls",
|
||||
@@ -28,9 +29,11 @@
|
||||
"docker:dev": "docker-compose -f docker-compose.yml -f docker-compose.dev.yml up",
|
||||
"docker:test": "docker-compose -f docker-compose.yml -f docker-compose.test.yml up",
|
||||
"types": "ts-node scripts/types.ts",
|
||||
"init": "ts-node scripts/init.ts",
|
||||
"swagger": "ts-node scripts/swagger.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"swagger-generate": "swagger-ui-watcher ./src/docs/swagger.json"
|
||||
"docs-watch": "swagger-ui-watcher ./src/docs/swagger.json",
|
||||
"docs": "ts-node scripts/swagger.ts && redoc-cli bundle ./src/docs/swagger.json -o ./docs/index.html"
|
||||
},
|
||||
"keywords": [
|
||||
"node",
|
||||
@@ -86,7 +89,7 @@
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"moment": "^2.24.0",
|
||||
"mongoose": "^5.7.7",
|
||||
"mongoose": "^8.5.2",
|
||||
"morgan": "^1.9.1",
|
||||
"nodemailer": "^6.3.1",
|
||||
"nunjucks": "^3.2.4",
|
||||
@@ -105,18 +108,22 @@
|
||||
"xss-clean": "^0.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^2.24.1",
|
||||
"@redocly/cli": "^1.18.0",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/faker": "^6.6.9",
|
||||
"@types/node": "^20.11.26",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"coveralls": "^3.0.7",
|
||||
"eslint": "^7.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb-base": "^14.0.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jest": "^24.0.1",
|
||||
"eslint-plugin-prettier": "^3.1.1",
|
||||
"eslint-plugin-security": "^1.4.0",
|
||||
"lint-staged": "^11.0.0",
|
||||
"node-mocks-http": "^1.8.0",
|
||||
"nodemon": "^2.0.0",
|
||||
"prettier": "^2.0.5",
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import config from '../src/config/config';
|
||||
import logger from '../src/config/logger';
|
||||
import { User } from '../src/models';
|
||||
import { tokenService } from '../src/services';
|
||||
import userService from '../src/services/user.service';
|
||||
import { userService } from '../src/services';
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 1) {
|
||||
console.error('Ошибка: Необходимо указать имя пользователя');
|
||||
logger.error('Ошибка: Необходимо указать имя пользователя');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -19,9 +20,9 @@ async function createServiceUser(username: string) {
|
||||
|
||||
const user = await userService.createServiceUser(username);
|
||||
const token = await tokenService.generateServiceAccessToken(user);
|
||||
console.log('token:', token.access.token);
|
||||
logger.log('token:', token.access.token);
|
||||
} catch (e: any) {
|
||||
console.log('Ошибка: ', e.message);
|
||||
logger.log('Ошибка: ', e.message);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import config from '../src/config/config';
|
||||
import { User } from '../src/models';
|
||||
import { tokenService } from '../src/services';
|
||||
import { userService } from '../src/services';
|
||||
import mongoose from 'mongoose';
|
||||
import { connectGenerator } from '../src/services/document.service';
|
||||
import crypto from 'crypto';
|
||||
import bcryptjs from 'bcryptjs';
|
||||
|
||||
const { compare, hash } = bcryptjs;
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
await connectGenerator();
|
||||
|
||||
await mongoose.connect(config.mongoose.url, config.mongoose.options);
|
||||
const wif = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3';
|
||||
const password = await crypto.createHash('sha256').update(wif).digest('hex');
|
||||
const hashed_password = await hash(password, 8);
|
||||
|
||||
console.log('password: ', hashed_password);
|
||||
|
||||
await userService.createUser({
|
||||
username: 'ant',
|
||||
email: 'dacom.dark.sun@gmail.com',
|
||||
password,
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
role: 'chairman',
|
||||
type: 'individual',
|
||||
individual_data: {
|
||||
first_name: 'Иван',
|
||||
last_name: 'Иванов',
|
||||
middle_name: 'Иванович',
|
||||
birthdate: '2000-01-01',
|
||||
phone: '+1234567890',
|
||||
email: 'ivanov.ivan@example.com',
|
||||
full_address: 'Переулок Иванов д. 1',
|
||||
},
|
||||
});
|
||||
|
||||
const user = await userService.getUserByUsername('ant');
|
||||
user.status = 'active';
|
||||
user.is_registered = true;
|
||||
await user.save();
|
||||
|
||||
const voskhod = await userService.createUser({
|
||||
username: 'voskhod',
|
||||
email: 'chairman.voskhod@gmail.com',
|
||||
password,
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
role: 'user',
|
||||
type: 'organization',
|
||||
organization_data: {
|
||||
is_cooperative: true,
|
||||
type: 'coop',
|
||||
short_name: '"ПК ТЕСТ-Восход"',
|
||||
full_name: 'Потребительский Кооператив "ТЕСТ-ВОСХОД"',
|
||||
represented_by: {
|
||||
first_name: 'Иван',
|
||||
last_name: 'Иванов',
|
||||
middle_name: 'Иванович',
|
||||
position: 'Председатель',
|
||||
based_on: 'Решения общего собрания №1',
|
||||
},
|
||||
country: 'Russia',
|
||||
city: 'Москва',
|
||||
full_address: 'Переулок Правды, дом 1',
|
||||
email: 'chairman.voskhod@gmail.com',
|
||||
phone: '+77077770707',
|
||||
details: {
|
||||
inn: '1234567890',
|
||||
ogrn: '1234567890123',
|
||||
},
|
||||
bank_account: {
|
||||
account_number: '40817810099910004312',
|
||||
currency: 'RUB',
|
||||
card_number: '1234567890123456',
|
||||
bank_name: 'Sberbank',
|
||||
details: {
|
||||
bik: '123456789',
|
||||
corr: '30101810400000000225',
|
||||
kpp: '123456789',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const coop = await userService.getUserByUsername('voskhod');
|
||||
coop.status = 'active';
|
||||
coop.is_registered = true;
|
||||
await coop.save();
|
||||
|
||||
console.log('ok');
|
||||
} catch (e: any) {
|
||||
console.log('Ошибка: ', e);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
init();
|
||||
@@ -0,0 +1,43 @@
|
||||
import config from '../../src/config/config';
|
||||
import { User } from '../../src/models';
|
||||
import { tokenService } from '../../src/services';
|
||||
import { userService } from '../../src/services';
|
||||
import mongoose from 'mongoose';
|
||||
import { connectGenerator } from '../../src/services/document.service';
|
||||
import crypto from 'crypto';
|
||||
import bcryptjs from 'bcryptjs';
|
||||
|
||||
const { compare, hash } = bcryptjs;
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
await connectGenerator();
|
||||
|
||||
await mongoose.connect(config.mongoose.url, config.mongoose.options);
|
||||
|
||||
const orgs_collection = mongoose.connection.collection('OrgData');
|
||||
const bankData_collection = mongoose.connection.collection('PaymentData');
|
||||
|
||||
const orgs = await orgs_collection.find().toArray();
|
||||
|
||||
for (const org of orgs) {
|
||||
await bankData_collection.insertOne({
|
||||
_created_at: org._created_at,
|
||||
username: org.username,
|
||||
method_id: 1,
|
||||
user_type: 'organization',
|
||||
method_type: 'bank_transfer',
|
||||
is_default: true,
|
||||
data: org.bank_account,
|
||||
deleted: false,
|
||||
block_num: org.block_num,
|
||||
});
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.log('Ошибка: ', e);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
init();
|
||||
@@ -3,12 +3,12 @@ import crypto from 'crypto';
|
||||
const { compare, hash } = bcryptjs;
|
||||
|
||||
async function generate() {
|
||||
const wif = '';
|
||||
const wif = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3';
|
||||
|
||||
const hash_from_wif = await crypto.createHash('sha256').update(wif).digest('hex');
|
||||
|
||||
const password = await hash(hash_from_wif, 8)
|
||||
console.log(password)
|
||||
console.log('hash_from_wif: ', hash_from_wif);
|
||||
const password = await hash(hash_from_wif, 8);
|
||||
console.log(password);
|
||||
}
|
||||
|
||||
generate();
|
||||
|
||||
@@ -7,7 +7,7 @@ async function types(): Promise<void> {
|
||||
// Configure your settings here
|
||||
const result = await convertFromDirectory({
|
||||
schemaDirectory: './src/validations',
|
||||
typeOutputDirectory: './src/types',
|
||||
typeOutputDirectory: './src/types/auto-generated',
|
||||
debug: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -14,15 +14,13 @@ import routes from './routes/v1/index';
|
||||
import { errorConverter, errorHandler } from './middlewares/error';
|
||||
import ApiError from './utils/ApiError';
|
||||
|
||||
import { connectGenerator } from './services/data.service';
|
||||
// import { connectGenerator } from './services/data.service';
|
||||
// Подключите генератор
|
||||
connectGenerator()
|
||||
.then(() => {
|
||||
console.log('Generator connected');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to connect generator:', error);
|
||||
});
|
||||
// connectGenerator()
|
||||
// .then(() => {})
|
||||
// .catch((error) => {
|
||||
// console.error('Failed to connect generator:', error);
|
||||
// });
|
||||
|
||||
const app = express();
|
||||
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import dotenv from 'dotenv';
|
||||
import Joi from 'joi';
|
||||
import path from 'path';
|
||||
// import { fileURLToPath } from 'url';
|
||||
// import { dirname, join } from 'path';
|
||||
|
||||
// const __filename = fileURLToPath(import.meta.url);
|
||||
// const __dirname = dirname(__filename);
|
||||
|
||||
dotenv.config({ path: path.join(__dirname, '../../.env') });
|
||||
|
||||
const envVarsSchema = Joi.object()
|
||||
.keys({
|
||||
NODE_ENV: Joi.string().valid('production', 'development', 'test').required(),
|
||||
BASE_URL: Joi.string().required(),
|
||||
SERVICE_USERNAME: Joi.string().required(),
|
||||
SERVICE_WIF: Joi.string().required(),
|
||||
PORT: Joi.number().default(3000),
|
||||
MONGODB_URL: Joi.string().required().description('Mongo DB url'),
|
||||
JWT_SECRET: Joi.string().required().description('JWT secret key'),
|
||||
@@ -28,6 +26,8 @@ const envVarsSchema = Joi.object()
|
||||
SMTP_USERNAME: Joi.string().description('username for email server'),
|
||||
SMTP_PASSWORD: Joi.string().description('password for email server'),
|
||||
EMAIL_FROM: Joi.string().description('the from field in the emails sent by the app'),
|
||||
|
||||
COOPNAME: Joi.string().description('имя аккаунта кооператива'),
|
||||
})
|
||||
.unknown();
|
||||
|
||||
@@ -39,13 +39,16 @@ if (error) {
|
||||
|
||||
export default {
|
||||
env: envVars.NODE_ENV,
|
||||
base_url: envVars.BASE_URL,
|
||||
port: envVars.PORT,
|
||||
service_wif: envVars.SERVICE_WIF,
|
||||
service_username: envVars.SERVICE_USERNAME,
|
||||
mongoose: {
|
||||
url: envVars.MONGODB_URL + (envVars.NODE_ENV === 'test' ? '-test' : ''),
|
||||
options: {
|
||||
useCreateIndex: true,
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
// useCreateIndex: true,
|
||||
// useNewUrlParser: true,
|
||||
// useUnifiedTopology: true,
|
||||
},
|
||||
},
|
||||
jwt: {
|
||||
@@ -66,4 +69,5 @@ export default {
|
||||
},
|
||||
from: envVars.EMAIL_FROM,
|
||||
},
|
||||
coopname: envVars.COOPNAME,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import winston from 'winston'
|
||||
import config from './config'
|
||||
import winston from 'winston';
|
||||
import config from './config';
|
||||
|
||||
const enumerateErrorFormat = winston.format((info) => {
|
||||
if (info instanceof Error) {
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import httpStatus from 'http-status';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
|
||||
import { authService, userService, tokenService, emailService, blockchainService } from '../services';
|
||||
import { authService, tokenService, emailService } from '../services';
|
||||
import { RForgotKey, RRefreshTokens, RResetKey, RVerifyEmail } from '../types';
|
||||
|
||||
const { CREATED, NO_CONTENT } = httpStatus;
|
||||
const { NO_CONTENT } = httpStatus;
|
||||
|
||||
export const login = catchAsync(async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
const user = await authService.loginUserWithUsernameAndPassword(username, password);
|
||||
const { now, signature, email } = req.body;
|
||||
|
||||
if (user.is_registered === false)
|
||||
throw new Error('Регистрация не была завершена. Пожалуйста, подождите решения совета или начните с начала. ')
|
||||
const user = await authService.loginUserWithSignature(email, now, signature);
|
||||
|
||||
const tokens = await tokenService.generateAuthTokens(user);
|
||||
res.send({ user, tokens });
|
||||
@@ -21,19 +20,19 @@ export const logout = catchAsync(async (req, res) => {
|
||||
res.status(NO_CONTENT).send();
|
||||
});
|
||||
|
||||
export const refreshTokens = catchAsync(async (req, res) => {
|
||||
const tokens = await authService.refreshAuth(req.body.refreshToken);
|
||||
res.send({ ...tokens });
|
||||
export const refreshTokens = catchAsync(async (req: RRefreshTokens, res: any) => {
|
||||
const tokens = await authService.refreshAuth(req.body);
|
||||
res.send(tokens);
|
||||
});
|
||||
|
||||
export const forgotPassword = catchAsync(async (req, res) => {
|
||||
const resetPasswordToken = await tokenService.generateResetPasswordToken(req.body.email);
|
||||
await emailService.sendResetPasswordEmail(req.body.email, resetPasswordToken);
|
||||
export const lostKey = catchAsync(async (req: RForgotKey, res) => {
|
||||
const resetKeyToken = await tokenService.generateResetPasswordToken(req.body.email);
|
||||
await emailService.sendResetPasswordEmail(req.body.email, resetKeyToken);
|
||||
res.status(NO_CONTENT).send();
|
||||
});
|
||||
|
||||
export const resetPassword = catchAsync(async (req, res) => {
|
||||
await authService.resetPassword(req.query.token, req.body.password);
|
||||
export const resetKey = catchAsync(async (req: RResetKey, res) => {
|
||||
await authService.resetKey(req.body.token, req.body.public_key);
|
||||
res.status(NO_CONTENT).send();
|
||||
});
|
||||
|
||||
@@ -43,7 +42,7 @@ export const sendVerificationEmail = catchAsync(async (req, res) => {
|
||||
res.status(NO_CONTENT).send();
|
||||
});
|
||||
|
||||
export const verifyEmail = catchAsync(async (req, res) => {
|
||||
export const verifyEmail = catchAsync(async (req: RVerifyEmail, res) => {
|
||||
await authService.verifyEmail(req.query.token);
|
||||
res.status(NO_CONTENT).send();
|
||||
});
|
||||
|
||||
@@ -1,36 +1,34 @@
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { coopService, dataService } from '../services';
|
||||
import { coopService, documentService } from '../services';
|
||||
import { Cooperative } from 'cooptypes';
|
||||
import { generator } from '../services/data.service';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import httpStatus from 'http-status';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
export const loadInfo = catchAsync(async (req, res) => {
|
||||
export const loadInfo = catchAsync(async (req: Request, res: Response) => {
|
||||
const cooperative = await coopService.loadInfo(String(process.env.COOPNAME));
|
||||
res.send(cooperative);
|
||||
});
|
||||
|
||||
export const loadContacts = catchAsync(async (req, res) => {
|
||||
export const loadContacts = catchAsync(async (req: Request, res: Response) => {
|
||||
const contacts = await coopService.loadContacts(String(process.env.COOPNAME));
|
||||
|
||||
res.send(contacts);
|
||||
});
|
||||
|
||||
export const loadAgenda = catchAsync(async (req, res) => {
|
||||
export const loadAgenda = catchAsync(async (req: Request, res: Response) => {
|
||||
const { coopname } = req.query;
|
||||
const agenda = await coopService.loadAgenda(coopname);
|
||||
const agenda = await coopService.loadAgenda(coopname as string);
|
||||
|
||||
const complexAgenda: Cooperative.Documents.IComplexAgenda[] = [];
|
||||
|
||||
for (const { action, table } of agenda) {
|
||||
const documents = await dataService.buildComplexDocument(action);
|
||||
const documents = await documentService.buildComplexDocument(action);
|
||||
if (documents.statement.document) complexAgenda.push({ documents, action, table });
|
||||
}
|
||||
|
||||
res.send(complexAgenda);
|
||||
});
|
||||
|
||||
export const loadStaff = catchAsync(async (req, res) => {
|
||||
export const loadStaff = catchAsync(async (req, res: Response) => {
|
||||
const { coopname } = req.query;
|
||||
const staff = await coopService.loadStaff(coopname);
|
||||
res.send(staff);
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { dataService } from '../services/index';
|
||||
import type { IGetDocuments, RGenerate, RGetDocuments } from '../types';
|
||||
import httpStatus from 'http-status';
|
||||
import pick from '../utils/pick';
|
||||
import { IGetResponse } from '../types/common';
|
||||
|
||||
export const generateDocument = catchAsync(async (req: RGenerate, res) => {
|
||||
const document = await dataService.generateDocument(req.body);
|
||||
|
||||
res.status(httpStatus.CREATED).send(document);
|
||||
});
|
||||
|
||||
export const getDocuments = catchAsync(async (req: RGetDocuments, res) => {
|
||||
let filter = req.query?.filter
|
||||
let limit = req.query?.limit as number
|
||||
let page = req.query?.page as number
|
||||
|
||||
const documents: IGetResponse = await dataService.queryDocuments(filter, page, limit);
|
||||
|
||||
res.status(httpStatus.OK).send(documents);
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { documentService } from '../services/index';
|
||||
import type { RGenerate, RGetDocuments } from '../types';
|
||||
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
export const generateDocument = catchAsync(async (req: RGenerate, res) => {
|
||||
const document = await documentService.generateDocument(req.body);
|
||||
|
||||
res.status(httpStatus.CREATED).send(document);
|
||||
});
|
||||
|
||||
export const getDocuments = catchAsync(async (req: RGetDocuments, res) => {
|
||||
const filter = req.query?.filter;
|
||||
const limit = req.query?.limit as number;
|
||||
const page = req.query?.page as number;
|
||||
|
||||
const documents = await documentService.queryDocuments(filter, page, limit);
|
||||
|
||||
res.status(httpStatus.OK).send(documents);
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
export * as authController from './auth.controller';
|
||||
export * as userController from './user.controller';
|
||||
export * as dataController from './data.controller';
|
||||
export * as orderController from './order.controller';
|
||||
export * as documentController from './document.controller';
|
||||
export * as paymentController from './payment.controller';
|
||||
export * as coopController from './coop.controller';
|
||||
export * as notifyController from './notify.controller';
|
||||
export * as monoController from './mono.controller';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import httpStatus from 'http-status';
|
||||
import { getMonoStatus } from '../services/mono.service';
|
||||
import { getBlockchainInfo } from '../services/blockchain.service';
|
||||
import { IHealthResponse } from '../types';
|
||||
import { Request, Response } from 'express';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
|
||||
export const getHealth = catchAsync(async (req: Request, res: Response) => {
|
||||
const status = await getMonoStatus();
|
||||
const blockchain = await getBlockchainInfo();
|
||||
|
||||
const result: IHealthResponse = {
|
||||
status,
|
||||
blockchain,
|
||||
};
|
||||
|
||||
res.status(httpStatus.OK).send(result);
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
import http from 'http-status';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { orderService } from '../services';
|
||||
import { ICreateDeposit, ICreateInitialPayment } from '../types';
|
||||
import { Request, Response } from 'express';
|
||||
import * as ip from 'ip';
|
||||
|
||||
// Список разрешенных IP-адресов
|
||||
// const allowedIPs = [
|
||||
// '185.71.76.0/27',
|
||||
// '185.71.77.0/27',
|
||||
// '77.75.153.0/25',
|
||||
// '77.75.156.11',
|
||||
// '77.75.156.35',
|
||||
// '77.75.154.128/25',
|
||||
// '2a02:5180::/32'
|
||||
// ];
|
||||
|
||||
|
||||
// if (process.env.NODE_ENV === 'development')
|
||||
// allowedIPs.push('127.0.0.1')
|
||||
|
||||
export const catchIPN = catchAsync(async (req: Request, res: Response) => {
|
||||
// const ipAddr = ((req.headers['x-real-ip'] as string) || req.ip || '').replace(/^::ffff:/, '');
|
||||
// console.log("ip: ", ipAddr);
|
||||
|
||||
// const isAllowed = allowedIPs.some(allowedIP => {
|
||||
// if (allowedIP.includes('/')) { // Если это диапазон
|
||||
// return ip.cidrSubnet(allowedIP).contains(ipAddr);
|
||||
// }
|
||||
// return allowedIP === ipAddr; // Если это конкретный IP
|
||||
// });
|
||||
|
||||
// if (!isAllowed) {
|
||||
// console.log("попытка доступа с запрещенного IP: ", ipAddr)
|
||||
// return res.status(http.FORBIDDEN).send('Доступ запрещен');
|
||||
// }
|
||||
|
||||
// console.log("on catch ipn", req.body);
|
||||
|
||||
res.status(http.OK).send();
|
||||
orderService.catchIPN(req.body);
|
||||
});
|
||||
|
||||
|
||||
export const createDeposit = catchAsync(async (req: any, res: Response) => {
|
||||
const username = req.user.username
|
||||
|
||||
const result = await orderService.createDeposit(username, req.body as ICreateDeposit)
|
||||
res.status(http.CREATED).send(result);
|
||||
})
|
||||
|
||||
export const createInitialPayment = catchAsync(async (req: any, res: Response) => {
|
||||
const username = req.user.username
|
||||
const result = await orderService.createInitialOrder(username, req.body as ICreateInitialPayment)
|
||||
res.status(http.CREATED).send(result);
|
||||
})
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import http from 'http-status';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { paymentService } from '../services';
|
||||
import {
|
||||
ICreateDeposit,
|
||||
ICreateInitialPayment,
|
||||
RDeletePaymentMethod,
|
||||
RGetListPaymentMethods,
|
||||
RSavePaymentMethod,
|
||||
} from '../types';
|
||||
import { Request, Response } from 'express';
|
||||
import * as ip from 'ip';
|
||||
import { IGetResponse } from '../types/common';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import httpStatus from 'http-status';
|
||||
import { Cooperative } from 'cooptypes';
|
||||
import { getUserByUsername } from '../services/user.service';
|
||||
import { User } from '../models';
|
||||
|
||||
// Список разрешенных IP-адресов
|
||||
// const allowedIPs = [
|
||||
// '185.71.76.0/27',
|
||||
// '185.71.77.0/27',
|
||||
// '77.75.153.0/25',
|
||||
// '77.75.156.11',
|
||||
// '77.75.156.35',
|
||||
// '77.75.154.128/25',
|
||||
// '2a02:5180::/32'
|
||||
// ];
|
||||
|
||||
// if (process.env.NODE_ENV === 'development')
|
||||
// allowedIPs.push('127.0.0.1')
|
||||
|
||||
export const catchIPN = catchAsync(async (req: Request, res: Response) => {
|
||||
// const ipAddr = ((req.headers['x-real-ip'] as string) || req.ip || '').replace(/^::ffff:/, '');
|
||||
// console.log("ip: ", ipAddr);
|
||||
|
||||
// const isAllowed = allowedIPs.some(allowedIP => {
|
||||
// if (allowedIP.includes('/')) { // Если это диапазон
|
||||
// return ip.cidrSubnet(allowedIP).contains(ipAddr);
|
||||
// }
|
||||
// return allowedIP === ipAddr; // Если это конкретный IP
|
||||
// });
|
||||
|
||||
// if (!isAllowed) {
|
||||
// console.log("попытка доступа с запрещенного IP: ", ipAddr)
|
||||
// return res.status(http.FORBIDDEN).send('Доступ запрещен');
|
||||
// }
|
||||
|
||||
// console.log("on catch ipn", req.body);
|
||||
|
||||
res.status(http.OK).send();
|
||||
paymentService.catchIPN(req.body);
|
||||
});
|
||||
|
||||
export const createDeposit = catchAsync(async (req: any, res: Response) => {
|
||||
const username = req.user.username;
|
||||
|
||||
const result = await paymentService.createDeposit(username, req.body as ICreateDeposit);
|
||||
res.status(http.CREATED).send(result);
|
||||
});
|
||||
|
||||
export const createInitialPayment = catchAsync(async (req: any, res: Response) => {
|
||||
const username = req.user.username;
|
||||
const result = await paymentService.createInitialOrder(username, req.body as ICreateInitialPayment);
|
||||
res.status(http.CREATED).send(result);
|
||||
});
|
||||
|
||||
export const listPaymentMethods = catchAsync(async (req: RGetListPaymentMethods, res) => {
|
||||
const filter = req.params?.username ? { username: req.params.username } : {};
|
||||
|
||||
const methods: IGetResponse<any> = await paymentService.listPaymentMethods({ ...filter });
|
||||
|
||||
res.status(httpStatus.OK).send(methods);
|
||||
});
|
||||
|
||||
export const addPaymentMethod = catchAsync(async (req: RSavePaymentMethod, res) => {
|
||||
if (req.params?.username != req.body.username)
|
||||
throw new ApiError(httpStatus.BAD_REQUEST, 'username в params и body должны совпадать');
|
||||
|
||||
const user = await getUserByUsername(req.body.username);
|
||||
|
||||
const method = req.body as any;
|
||||
|
||||
if (method.method_type === 'sbp' && !method.data.phone)
|
||||
throw new ApiError(httpStatus.BAD_REQUEST, 'Не указан телефон для метода СБП');
|
||||
|
||||
if (method.method_type === 'bank_transfer' && !method.data.account_number)
|
||||
throw new ApiError(httpStatus.BAD_REQUEST, 'Не верно указаны реквизиты для банковского платежа');
|
||||
|
||||
const paymentData: Cooperative.Payments.IPaymentData = {
|
||||
username: req.body.username,
|
||||
method_id: req.body.method_id,
|
||||
user_type: user.type,
|
||||
method_type: req.body.method_type,
|
||||
is_default: false,
|
||||
data: req.body.data,
|
||||
};
|
||||
|
||||
await paymentService.savePaymentMethod(paymentData);
|
||||
|
||||
res.status(httpStatus.OK).send();
|
||||
});
|
||||
|
||||
export const deletePaymentMethod = catchAsync(async (req: RDeletePaymentMethod, res) => {
|
||||
await paymentService.deletePaymentMethod({
|
||||
username: req.params?.username,
|
||||
method_id: req.body.method_id,
|
||||
});
|
||||
|
||||
res.status(httpStatus.OK).send();
|
||||
});
|
||||
@@ -1,11 +1,12 @@
|
||||
import http, { CREATED } from 'http-status';
|
||||
import http from 'http-status';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { userService, blockchainService, tokenService, orderService } from '../services';
|
||||
import { IJoinCooperative, RCreateUser, RJoinCooperative } from '../types';
|
||||
import { userService, tokenService } from '../services';
|
||||
import { RCreateUser, RJoinCooperative } from '../types';
|
||||
import httpStatus from 'http-status';
|
||||
import pick from '../utils/pick';
|
||||
import { IGetResponse } from '../types/common';
|
||||
import { IUser } from '../models/user.model';
|
||||
|
||||
/**
|
||||
* Порядок регистрации:
|
||||
@@ -39,23 +40,23 @@ export const getUsers = catchAsync(async (req, res) => {
|
||||
const filter = pick(req.query, ['username', 'role']);
|
||||
const options = pick(req.query, ['sortBy', 'limit', 'page']);
|
||||
|
||||
filter.role = 'user';
|
||||
|
||||
const users = await userService.queryUsers(filter, options);
|
||||
const result = {} as IGetResponse<IUser>;
|
||||
|
||||
const result = {} as IGetResponse; //
|
||||
|
||||
//TODO wrong format answer
|
||||
const data = [] as any;
|
||||
for await (let user of users.results) {
|
||||
for await (const user of users.results) {
|
||||
const json = user.toJSON();
|
||||
json.private_data = (await user.getPrivateData()) || {};
|
||||
data.push(json);
|
||||
if (user.type != 'service') {
|
||||
json.private_data = (await user.getPrivateData()) || {};
|
||||
data.push(json);
|
||||
}
|
||||
}
|
||||
|
||||
result.results = data;
|
||||
result.limit = users.limit;
|
||||
result.page = users.page;
|
||||
result.totalPages = users.totalPages;
|
||||
result.totalResults = users.totalResults;
|
||||
|
||||
res.send(result);
|
||||
});
|
||||
@@ -73,7 +74,10 @@ export const getUser = catchAsync(async (req, res) => {
|
||||
});
|
||||
|
||||
export const updateUser = catchAsync(async (req, res) => {
|
||||
const user = await userService.updateUserById(req.params.username, req.body);
|
||||
if (req.user.role !== 'member' && req.user.role !== 'chairman')
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Только председатель или член совета может обновить данные пользователя');
|
||||
|
||||
const user = await userService.updateUserByUsername(req.params.username, req.body);
|
||||
res.send(user);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
export * from './types';
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
import app from './app';
|
||||
import config from './config/config';
|
||||
import logger from './config/logger';
|
||||
import { updateAuth } from './services/auth.service';
|
||||
import { connectGenerator } from './services/data.service';
|
||||
import { connectGenerator } from './services/document.service';
|
||||
import { initSocketConnection } from './controllers/ws.controller';
|
||||
import { monoService } from './services';
|
||||
|
||||
const SERVER_URL: string = process.env.SOCKET_SERVER || 'http://localhost:2222';
|
||||
|
||||
let server: any;
|
||||
|
||||
mongoose.connect(config.mongoose.url, config.mongoose.options).then(async () => {
|
||||
mongoose.connect(config.mongoose.url).then(async () => {
|
||||
logger.info('Connected to MongoDB');
|
||||
|
||||
//подключаемся к хранилищу приватных данных
|
||||
await monoService.init();
|
||||
|
||||
// подключаемся к хранилищу приватных данных
|
||||
await connectGenerator();
|
||||
|
||||
//подключаемся к ws-серверу
|
||||
initSocketConnection(SERVER_URL);
|
||||
// подключаемся к ws-серверу
|
||||
await initSocketConnection(SERVER_URL);
|
||||
|
||||
server = app.listen(config.port, () => {
|
||||
logger.info(`Listening to port ${config.port}`);
|
||||
|
||||
@@ -15,11 +15,10 @@ const verifyCallback = (req, resolve, reject, requiredRights) => async (err, use
|
||||
const userRights = roleRights.get(user.role);
|
||||
const hasRequiredRights = requiredRights.every((requiredRight) => userRights?.includes(requiredRight));
|
||||
|
||||
// if (process.env.IS_PRODUCTION) {
|
||||
if (!hasRequiredRights && req.params.username !== user.username) {
|
||||
//
|
||||
return reject(new ApiError(FORBIDDEN, 'Недостаточно прав доступа'));
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
resolve();
|
||||
|
||||
@@ -10,12 +10,12 @@ export const errorConverter = (err, req, res, next) => {
|
||||
if (!(error instanceof ApiError)) {
|
||||
if (error instanceof RpcError) {
|
||||
const message = error.json.error.details[0].message.replace('assertion failure with message: ', ''); // Получаем первую строку ошибки
|
||||
const statusCode = error.json.code
|
||||
const subCode = error.json.error.code
|
||||
const statusCode = error.json.code;
|
||||
const subCode = error.json.error.code;
|
||||
error = new ApiError(statusCode, message, true, undefined, subCode);
|
||||
|
||||
} else {
|
||||
const statusCode = error.statusCode || (error instanceof mongoose.Error ? httpStatus.BAD_REQUEST : httpStatus.INTERNAL_SERVER_ERROR);
|
||||
const statusCode =
|
||||
error.statusCode || (error instanceof mongoose.Error ? httpStatus.BAD_REQUEST : httpStatus.INTERNAL_SERVER_ERROR);
|
||||
const message = error.message || httpStatus[statusCode];
|
||||
error = new ApiError(statusCode, message, false, err.stack);
|
||||
}
|
||||
@@ -24,8 +24,7 @@ export const errorConverter = (err, req, res, next) => {
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
export const errorHandler = (err, req, res, next) => {
|
||||
|
||||
export const errorHandler = (err, req, res, next?) => {
|
||||
let { statusCode, message } = err;
|
||||
if (config.env === 'production' && !err.isOperational) {
|
||||
statusCode = httpStatus.INTERNAL_SERVER_ERROR;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export { default as Token } from './token.model';
|
||||
export { default as User } from './user.model';
|
||||
export { default as Order } from './order.model';
|
||||
export { default as Mono } from './mono.model';
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import mongoose, { Schema } from 'mongoose';
|
||||
import { paginate, toJSON } from './plugins';
|
||||
import { IHealthStatus } from '../types';
|
||||
|
||||
export interface IMono {
|
||||
coopname: string;
|
||||
status: IHealthStatus;
|
||||
}
|
||||
|
||||
const Status = {
|
||||
Install: 'install',
|
||||
Active: 'active',
|
||||
Maintenance: 'maintenance',
|
||||
};
|
||||
|
||||
const MonoSchema = new Schema<IMono>({
|
||||
coopname: { type: String, required: true },
|
||||
status: { type: String, required: true, enum: Object.values(Status) },
|
||||
});
|
||||
|
||||
MonoSchema.plugin(toJSON);
|
||||
MonoSchema.plugin(paginate);
|
||||
|
||||
const MonoModel = mongoose.model<IMono>('Mono', MonoSchema);
|
||||
|
||||
export default MonoModel;
|
||||
@@ -5,9 +5,9 @@ interface IOrder extends Document {
|
||||
creator: string;
|
||||
type: string;
|
||||
data: {
|
||||
username: string
|
||||
provider: string
|
||||
quantity: string
|
||||
username: string;
|
||||
provider: string;
|
||||
quantity: string;
|
||||
};
|
||||
order_id?: number;
|
||||
payed?: boolean;
|
||||
@@ -16,59 +16,61 @@ interface IOrder extends Document {
|
||||
expired_at?: Date;
|
||||
}
|
||||
|
||||
const orderSchema = new Schema<IOrder>({
|
||||
creator: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data: {
|
||||
provider: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
username: {
|
||||
const orderSchema = new Schema<IOrder>(
|
||||
{
|
||||
creator: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
quantity: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
order_id: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
payed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
delivered: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
expired_at: {
|
||||
type: Date,
|
||||
default: () => {
|
||||
const now = new Date();
|
||||
now.setDate(now.getDate() + 1); // Увеличиваем на одни сутки
|
||||
return now;
|
||||
required: true,
|
||||
},
|
||||
data: {
|
||||
provider: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
username: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
quantity: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
order_id: {
|
||||
type: Number,
|
||||
required: false,
|
||||
},
|
||||
payed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
delivered: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
expired_at: {
|
||||
type: Date,
|
||||
default: () => {
|
||||
const now = new Date();
|
||||
now.setDate(now.getDate() + 1); // Увеличиваем на одни сутки
|
||||
return now;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
});
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
// add plugin that converts mongoose to json
|
||||
orderSchema.plugin(toJSON);
|
||||
|
||||
@@ -28,7 +28,7 @@ const toJSON = (schema) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ret.id = ret._id.toString();
|
||||
ret.id = ret._id.toString();
|
||||
delete ret._id;
|
||||
delete ret.__v;
|
||||
delete ret.createdAt;
|
||||
|
||||
@@ -39,6 +39,7 @@ const tokenSchema = new Schema<IToken>(
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
toJSON: { virtuals: true },
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import mongoose, { Document, Schema, model, Model } from 'mongoose';
|
||||
import mongoose, { Schema, model, Model } from 'mongoose';
|
||||
import validator from 'validator/index';
|
||||
import bcryptjs from 'bcryptjs';
|
||||
import { toJSON, paginate } from './plugins/index';
|
||||
import { roles } from '../config/roles';
|
||||
import { generator } from '../services/data.service';
|
||||
import { generator } from '../services/document.service';
|
||||
import { Cooperative } from 'cooptypes';
|
||||
|
||||
const { isEmail } = validator;
|
||||
const { compare, hash } = bcryptjs;
|
||||
|
||||
export interface IUser extends Document {
|
||||
export interface IUser {
|
||||
username: string;
|
||||
status: 'created' | 'joined' | 'payed' | 'registered' | 'active' | 'failed' | 'blocked';
|
||||
message: string;
|
||||
is_registered: boolean;
|
||||
has_account: boolean;
|
||||
type: 'individual' | 'entrepreneur' | 'organization';
|
||||
public_key: string;
|
||||
referer: string;
|
||||
@@ -23,7 +24,7 @@ export interface IUser extends Document {
|
||||
is_email_verified: boolean;
|
||||
statement: {
|
||||
hash: string;
|
||||
meta: any;
|
||||
meta: object;
|
||||
public_key: string;
|
||||
signature: string;
|
||||
};
|
||||
@@ -134,6 +135,7 @@ const userSchema = new Schema<IUser, IUserModel>(
|
||||
},
|
||||
},
|
||||
{
|
||||
minimize: false,
|
||||
timestamps: true,
|
||||
toJSON: { virtuals: true },
|
||||
}
|
||||
@@ -152,15 +154,23 @@ userSchema.statics.isEmailTaken = async function (email) {
|
||||
userSchema.methods.getPrivateData = async function (): Promise<
|
||||
Cooperative.Users.IIndividualData | Cooperative.Users.IEntrepreneurData | Cooperative.Users.IOrganizationData | null
|
||||
> {
|
||||
return await generator.get(this.type, { username: this.username });
|
||||
const result = (await generator.get(this.type, { username: this.username })) as
|
||||
| Cooperative.Users.IIndividualData
|
||||
| Cooperative.Users.IEntrepreneurData
|
||||
| Cooperative.Users.IOrganizationData
|
||||
| null;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
userSchema.methods.isPasswordMatch = async function (password) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const user = this;
|
||||
return compare(password, user.password);
|
||||
};
|
||||
|
||||
userSchema.pre('save', async function (next) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const user = this;
|
||||
if (user.isModified('password')) {
|
||||
user.password = await hash(user.password, 8);
|
||||
|
||||
@@ -7,13 +7,13 @@ import auth from '../../middlewares/auth';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post('/login', validate(authValidation.login), authController.login);
|
||||
router.post('/logout', validate(authValidation.logout), authController.logout);
|
||||
router.post('/refresh-tokens', validate(authValidation.refreshTokens), authController.refreshTokens);
|
||||
router.post('/forgot-password', validate(authValidation.forgotPassword), authController.forgotPassword);
|
||||
router.post('/reset-password', validate(authValidation.resetPassword), authController.resetPassword);
|
||||
router.post('/login', validate(authValidation.RLogin), authController.login);
|
||||
router.post('/logout', validate(authValidation.RLogout), authController.logout);
|
||||
router.post('/refresh-tokens', validate(authValidation.RRefreshTokens), authController.refreshTokens);
|
||||
router.post('/lost-key', validate(authValidation.RForgotKey), authController.lostKey);
|
||||
router.post('/reset-key', validate(authValidation.RResetKey), authController.resetKey);
|
||||
router.post('/send-verification-email', auth(), authController.sendVerificationEmail);
|
||||
router.post('/verify-email', validate(authValidation.verifyEmail), authController.verifyEmail);
|
||||
router.post('/verify-email', validate(authValidation.RVerifyEmail), authController.verifyEmail);
|
||||
|
||||
export default router;
|
||||
|
||||
|
||||
@@ -2,15 +2,14 @@ import { Router } from 'express';
|
||||
import auth from '../../middlewares/auth';
|
||||
import validate from '../../middlewares/validate';
|
||||
import * as coopValidation from '../../validations/coop.validation';
|
||||
// import { loadAgenda, loadStaff, } from '../../validations/coop.validation';
|
||||
import * as coopController from '../../controllers/coop.controller';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.route('/agenda').get(auth('loadAgenda'), validate(coopValidation.loadAgenda), coopController.loadAgenda);
|
||||
router.route('/staff').get(auth('loadStaff'), validate(coopValidation.loadStaff), coopController.loadStaff);
|
||||
|
||||
router.route('/contacts').get(coopController.loadContacts);
|
||||
router.route('/info').get(auth('loadInfo'), coopController.loadInfo);
|
||||
|
||||
router.route('/contacts').get(coopController.loadContacts);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import express from "express"
|
||||
import auth from "../../middlewares/auth"
|
||||
import validate from "../../middlewares/validate"
|
||||
import * as dataValidation from "../../validations/data.validation"
|
||||
import { dataController } from "../../controllers"
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router
|
||||
.route("/generate")
|
||||
.post(
|
||||
auth(),
|
||||
validate(dataValidation.RGenerate),
|
||||
dataController.generateDocument
|
||||
)
|
||||
|
||||
router
|
||||
.route("/get-documents")
|
||||
.get(
|
||||
auth("getDocuments"),
|
||||
validate(dataValidation.RGetDocuments),
|
||||
dataController.getDocuments
|
||||
)
|
||||
router
|
||||
.route("/get-my-documents")
|
||||
.get(
|
||||
auth(),
|
||||
validate(dataValidation.RGetDocuments),
|
||||
dataController.getDocuments
|
||||
)
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,17 @@
|
||||
import express from 'express';
|
||||
import auth from '../../middlewares/auth';
|
||||
import validate from '../../middlewares/validate';
|
||||
import * as documentValidation from '../../validations/document.validation';
|
||||
import { documentController } from '../../controllers';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route('/generate').post(auth(), validate(documentValidation.RGenerate), documentController.generateDocument);
|
||||
|
||||
router
|
||||
.route('/get-documents')
|
||||
.get(auth('getDocuments'), validate(documentValidation.RGetDocuments), documentController.getDocuments);
|
||||
|
||||
router.route('/get-my-documents').get(auth(), validate(documentValidation.RGetDocuments), documentController.getDocuments);
|
||||
|
||||
export default router;
|
||||
@@ -2,9 +2,10 @@ import { Router } from 'express';
|
||||
import authRoute from './auth.route';
|
||||
import userRoute from './user.route';
|
||||
import docsRoute from './docs.route';
|
||||
import orderRoute from './order.route';
|
||||
import paymentRoute from './payment.route';
|
||||
import coopRoute from './coop.route';
|
||||
import dataRoute from './data.route';
|
||||
import monoRoute from './mono.route';
|
||||
import dataRoute from './document.route';
|
||||
import notifyRoute from './notify.route';
|
||||
|
||||
import config from '../../config/config';
|
||||
@@ -12,6 +13,10 @@ import config from '../../config/config';
|
||||
const router = Router();
|
||||
|
||||
const defaultRoutes = [
|
||||
{
|
||||
path: '/mono',
|
||||
route: monoRoute,
|
||||
},
|
||||
{
|
||||
path: '/auth',
|
||||
route: authRoute,
|
||||
@@ -21,12 +26,12 @@ const defaultRoutes = [
|
||||
route: userRoute,
|
||||
},
|
||||
{
|
||||
path: '/data',
|
||||
path: '/documents',
|
||||
route: dataRoute,
|
||||
},
|
||||
{
|
||||
path: '/orders',
|
||||
route: orderRoute,
|
||||
path: '/payments',
|
||||
route: paymentRoute,
|
||||
},
|
||||
{
|
||||
path: '/coop',
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Router } from 'express';
|
||||
import { monoController } from '../../controllers';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.route('/health').get(monoController.getHealth);
|
||||
|
||||
export default router;
|
||||
+19
-5
@@ -1,7 +1,7 @@
|
||||
import { Router } from 'express';
|
||||
import { orderController } from '../../controllers';
|
||||
import { paymentController } from '../../controllers';
|
||||
import validate from '../../middlewares/validate';
|
||||
import * as orderValidation from '../../validations/order.validation';
|
||||
import * as paymentValidation from '../../validations/payment.validation';
|
||||
import auth from '../../middlewares/auth';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
@@ -41,7 +41,7 @@ const router = Router();
|
||||
* 200:
|
||||
* description: Успешное выполнение запроса.
|
||||
*/
|
||||
router.route('/ipn').post(validate(orderValidation.RRecieveIPN), orderController.catchIPN);
|
||||
router.route('/ipn').post(validate(paymentValidation.RRecieveIPN), paymentController.catchIPN);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@@ -66,7 +66,9 @@ router.route('/ipn').post(validate(orderValidation.RRecieveIPN), orderController
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/ICreatedPayment'
|
||||
*/
|
||||
router.route('/initial').post(auth(), validate(orderValidation.RCreateInitialPayment), orderController.createInitialPayment);
|
||||
router
|
||||
.route('/initial')
|
||||
.post(auth(), validate(paymentValidation.RCreateInitialPayment), paymentController.createInitialPayment);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@@ -91,6 +93,18 @@ router.route('/initial').post(auth(), validate(orderValidation.RCreateInitialPay
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/ICreatedPayment'
|
||||
*/
|
||||
router.route('/deposit').post(auth(), validate(orderValidation.RCreateDeposit), orderController.createDeposit);
|
||||
router.route('/deposit').post(auth(), validate(paymentValidation.RCreateDeposit), paymentController.createDeposit);
|
||||
|
||||
router
|
||||
.route('/methods/:username?')
|
||||
.get(auth('getUsers'), validate(paymentValidation.RGetListPaymentMethods), paymentController.listPaymentMethods);
|
||||
|
||||
router
|
||||
.route('/methods/:username/add')
|
||||
.post(auth('manageUsers'), validate(paymentValidation.RSavePaymentMethod), paymentController.addPaymentMethod);
|
||||
|
||||
router
|
||||
.route('/methods/:username/delete')
|
||||
.post(auth('manageUsers'), validate(paymentValidation.RDeletePaymentMethod), paymentController.deletePaymentMethod);
|
||||
|
||||
export default router;
|
||||
@@ -1,66 +1,63 @@
|
||||
import httpStatus from 'http-status';
|
||||
import * as tokenService from './token.service';
|
||||
import userService from './user.service';
|
||||
import { getSoviet } from './blockchain.service';
|
||||
import * as userService from './user.service';
|
||||
import Token from '../models/token.model';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import { tokenTypes } from '../config/tokens';
|
||||
import { IRefreshTokens } from '../types';
|
||||
import { getUserByEmail } from './user.service';
|
||||
import { Bytes, Checksum256, Signature } from '@wharfkit/antelope';
|
||||
import { getBlockchainAccount, getBlockchainInfo, hasActiveKey } from '../services/blockchain.service';
|
||||
import config from '../config/config';
|
||||
import { blockchainService } from '.';
|
||||
|
||||
export const updateAuth = async () => {
|
||||
try {
|
||||
const board = await getSoviet(process.env.COOPNAME);
|
||||
// TODO снимать права с тех, кто уже не в совете
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const member of board.members) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const user = await userService.getUserByUsername(member.username);
|
||||
if (member.position === 'chairman' && !user) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
// await userService.createUser({
|
||||
// username: member.username,
|
||||
// public_key: '-',
|
||||
// email: process.env.CHAIRMAN_EMAIL ,
|
||||
// password: process.env.CHAIRMAN_PASSWORD as string,
|
||||
// is_registered: true,
|
||||
// is_organization: false,
|
||||
// user_profile: {
|
||||
// first_name: 'Имя',
|
||||
// last_name: 'Фамилия',
|
||||
// middle_name: 'Отчество',
|
||||
// birthday: '23-42-3423',
|
||||
// phone: '+7902294404',
|
||||
// },
|
||||
// signature: '-',
|
||||
// signature_hash: '-',
|
||||
// role: 'chairman',
|
||||
// });
|
||||
} else if (member.position === 'chairman' && user) {
|
||||
user.role = 'chairman';
|
||||
user.password = process.env.CHAIRMAN_PASSWORD || 'password';
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await user.save();
|
||||
} else if (user) {
|
||||
user.role = 'admin';
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await user.save();
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Ошибка при автоматической проверке целевой авторизации: ', e.message);
|
||||
export const loginUserWithSignature = async (email, now, signature) => {
|
||||
const user = await getUserByEmail(email);
|
||||
|
||||
if (!user) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Пользователь не найден');
|
||||
}
|
||||
|
||||
const bytes = Bytes.fromString(now, 'utf8');
|
||||
const checksum = Checksum256.hash(bytes);
|
||||
const wharf_signature = Signature.from(signature);
|
||||
const publicKey = wharf_signature.recoverDigest(checksum).toLegacyString();
|
||||
|
||||
const info = await getBlockchainInfo();
|
||||
const blockchainDate = new Date(info.head_block_time).getTime();
|
||||
const userData = new Date(now).getTime();
|
||||
|
||||
const differenceInSeconds = (blockchainDate - userData) / 1000;
|
||||
|
||||
if (differenceInSeconds > 30) {
|
||||
throw new ApiError(httpStatus.BAD_REQUEST, 'Время подписи и время блокчейна превышает допустимое расхождение');
|
||||
}
|
||||
|
||||
if (config.env !== 'test') {
|
||||
try {
|
||||
const blockchainAccount = await getBlockchainAccount(user.username);
|
||||
|
||||
const hasKey = hasActiveKey(blockchainAccount, publicKey);
|
||||
|
||||
if (!hasKey) throw new ApiError(httpStatus.UNAUTHORIZED, 'Неверный приватный ключ');
|
||||
} catch (e) {
|
||||
throw new ApiError(httpStatus.BAD_REQUEST, 'Аккаунт в блокчейне не найден');
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
/**
|
||||
* Login with username and password
|
||||
* @param {string} username
|
||||
* Login with email and password
|
||||
* @param {string} email
|
||||
* @param {string} password
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
export const loginUserWithUsernameAndPassword = async (username, password) => {
|
||||
const user = await userService.getUserByUsername(username);
|
||||
export const loginUserWithEmailAndPassword = async (email, password) => {
|
||||
const user = await userService.getUserByEmail(email);
|
||||
if (!user || !(await user.isPasswordMatch(password))) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Incorrect username or password');
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Incorrect email or password');
|
||||
}
|
||||
return user;
|
||||
};
|
||||
@@ -75,7 +72,7 @@ export const logout = async (refreshToken) => {
|
||||
if (!refreshTokenDoc) {
|
||||
throw new ApiError(httpStatus.NOT_FOUND, 'Not found');
|
||||
}
|
||||
await refreshTokenDoc.remove();
|
||||
await refreshTokenDoc.deleteOne();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -83,16 +80,14 @@ export const logout = async (refreshToken) => {
|
||||
* @param {string} refreshToken
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export const refreshAuth = async (refreshToken) => {
|
||||
export const refreshAuth = async (data: IRefreshTokens) => {
|
||||
try {
|
||||
const refreshTokenDoc = await tokenService.verifyToken(refreshToken, tokenTypes.REFRESH);
|
||||
const user = await userService.getUserByUsername(refreshTokenDoc.user);
|
||||
|
||||
const refreshTokenDoc = await tokenService.verifyToken(data.refreshToken, tokenTypes.REFRESH);
|
||||
const user = await userService.getUserById(refreshTokenDoc.user);
|
||||
if (!user) {
|
||||
throw new Error();
|
||||
}
|
||||
await refreshTokenDoc.remove();
|
||||
|
||||
await refreshTokenDoc.deleteOne();
|
||||
return tokenService.generateAuthTokens(user);
|
||||
} catch (error) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate');
|
||||
@@ -100,21 +95,32 @@ export const refreshAuth = async (refreshToken) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset password
|
||||
* @param {string} resetPasswordToken
|
||||
* @param {string} newPassword
|
||||
* Reset key
|
||||
* @param {string} resetKeyToken
|
||||
* @param {string} publicKey
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const resetPassword = async (resetPasswordToken, newPassword) => {
|
||||
export const resetKey = async (resetKeyToken, publicKey) => {
|
||||
try {
|
||||
const resetPasswordTokenDoc = await tokenService.verifyToken(resetPasswordToken, tokenTypes.RESET_PASSWORD);
|
||||
const user = await userService.getUserByUsername(resetPasswordTokenDoc.user);
|
||||
const resetPasswordTokenDoc = await tokenService.verifyToken(resetKeyToken, tokenTypes.RESET_PASSWORD);
|
||||
|
||||
const user = await userService.getUserById(resetPasswordTokenDoc.user);
|
||||
if (!user) {
|
||||
throw new Error();
|
||||
}
|
||||
await userService.updateUserById(user.username, { password: newPassword });
|
||||
await Token.deleteMany({ user: user.username, type: tokenTypes.RESET_PASSWORD });
|
||||
|
||||
await blockchainService.changeKey({
|
||||
coopname: config.coopname,
|
||||
changer: config.service_username,
|
||||
username: user.username,
|
||||
public_key: publicKey,
|
||||
});
|
||||
|
||||
await userService.updateUserById(user._id, { public_key: publicKey });
|
||||
|
||||
await Token.deleteMany({ user: user._id, type: tokenTypes.RESET_PASSWORD });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Password reset failed');
|
||||
}
|
||||
};
|
||||
@@ -127,12 +133,12 @@ export const resetPassword = async (resetPasswordToken, newPassword) => {
|
||||
export const verifyEmail = async (verifyEmailToken) => {
|
||||
try {
|
||||
const verifyEmailTokenDoc = await tokenService.verifyToken(verifyEmailToken, tokenTypes.VERIFY_EMAIL);
|
||||
const user = await userService.getUserByUsername(verifyEmailTokenDoc.user);
|
||||
const user = await userService.getUserById(verifyEmailTokenDoc.user);
|
||||
if (!user) {
|
||||
throw new Error();
|
||||
}
|
||||
await Token.deleteMany({ user: user.username, type: tokenTypes.VERIFY_EMAIL });
|
||||
await userService.updateUserById(user.username, { is_email_verified: true });
|
||||
await Token.deleteMany({ user: user._id, type: tokenTypes.VERIFY_EMAIL });
|
||||
await userService.updateUserById(user._id, { is_email_verified: true });
|
||||
} catch (error) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Email verification failed');
|
||||
}
|
||||
|
||||
@@ -6,9 +6,30 @@ import EosApi from 'eosjs-api';
|
||||
import getInternalAction from '../utils/getInternalAction';
|
||||
import { GatewayContract, RegistratorContract, SovietContract } from 'cooptypes';
|
||||
import { IUser } from '../models/user.model';
|
||||
import { GetAccountResult, GetInfoResult } from 'eosjs/dist/eosjs-rpc-interfaces';
|
||||
import config from '../config/config';
|
||||
|
||||
const rpc = new JsonRpc(process.env.BLOCKCHAIN_RPC as string, { fetch });
|
||||
|
||||
function hasActiveKey(account, publicKey) {
|
||||
const activePermissions = account.permissions.find((p: any) => p.perm_name === 'active');
|
||||
if (!activePermissions) return false;
|
||||
|
||||
return activePermissions.required_auth.keys.some((key: any) => key.key === publicKey);
|
||||
}
|
||||
|
||||
async function getBlockchainInfo(): Promise<GetInfoResult> {
|
||||
const api = getApi();
|
||||
|
||||
return await api.getInfo({});
|
||||
}
|
||||
|
||||
async function getBlockchainAccount(username): Promise<GetAccountResult> {
|
||||
const api = getApi();
|
||||
|
||||
return await api.getAccount(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить инстанс для осуществления транзакции в блокчейн.
|
||||
* @param {username} - авторизация от аккаунта
|
||||
@@ -22,7 +43,7 @@ async function getInstance(wif) {
|
||||
return api;
|
||||
}
|
||||
|
||||
async function getApi() {
|
||||
function getApi() {
|
||||
const options = {
|
||||
httpEndpoint: process.env.BLOCKCHAIN_RPC, // default, null for cold-storage
|
||||
verbose: false, // API logging
|
||||
@@ -245,6 +266,34 @@ async function failOrder(data) {
|
||||
);
|
||||
}
|
||||
|
||||
export async function changeKey(data: RegistratorContract.Actions.ChangeKey.IChangeKey) {
|
||||
const eos = await getInstance(config.service_wif);
|
||||
|
||||
const actions = [
|
||||
{
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.ChangeKey.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.service_username,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data,
|
||||
},
|
||||
];
|
||||
|
||||
await eos.transact(
|
||||
{
|
||||
actions,
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function getSoviet(coopname) {
|
||||
const api = await getApi();
|
||||
|
||||
@@ -271,4 +320,7 @@ export {
|
||||
failOrder,
|
||||
completeOrder,
|
||||
getSoviet,
|
||||
getBlockchainInfo,
|
||||
getBlockchainAccount,
|
||||
hasActiveKey,
|
||||
};
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { getActions } from '../utils/getFetch';
|
||||
import { generator } from './data.service';
|
||||
import { generator } from './document.service';
|
||||
import { userService, blockchainService } from './index';
|
||||
import { Cooperative, SovietContract } from 'cooptypes';
|
||||
import { Cooperative, RegistratorContract, SovietContract } from 'cooptypes';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import httpStatus from 'http-status';
|
||||
import logger from '../config/logger';
|
||||
|
||||
export const loadAgenda = async (coopname: string): Promise<Cooperative.Documents.IAgenda[]> => {
|
||||
const api = await blockchainService.getApi();
|
||||
@@ -63,11 +64,32 @@ export const loadInfo = async (coopname: string) => {
|
||||
export const loadContacts = async (coopname: string) => {
|
||||
const cooperative: Cooperative.Model.ICooperativeData | null = await generator.constructCooperative(coopname);
|
||||
|
||||
if (!cooperative) throw new Error('Кооператив не найден');
|
||||
if (!cooperative) throw new ApiError(httpStatus.NOT_FOUND, 'Кооператив не найден');
|
||||
|
||||
const announce = cooperative?.announce
|
||||
? JSON.parse(cooperative.announce)
|
||||
: { phone: cooperative?.phone, email: cooperative?.email };
|
||||
const api = await blockchainService.getApi();
|
||||
const coopAccount = (
|
||||
await blockchainService.lazyFetch(
|
||||
api,
|
||||
RegistratorContract.contractName.production,
|
||||
RegistratorContract.contractName.production,
|
||||
RegistratorContract.Tables.Accounts.tableName,
|
||||
coopname,
|
||||
coopname,
|
||||
1
|
||||
)
|
||||
)[0];
|
||||
|
||||
if (!coopAccount) throw new ApiError(httpStatus.NOT_FOUND, 'Аккаунт не найден');
|
||||
|
||||
let announce: Cooperative.Users.IAccountMeta = { phone: cooperative?.phone, email: cooperative?.email };
|
||||
|
||||
if (coopAccount.meta) {
|
||||
try {
|
||||
announce = JSON.parse(coopAccount.meta);
|
||||
} catch (e: any) {
|
||||
logger.warn(`Ошибка при получении контактов: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
full_name: cooperative?.full_name,
|
||||
|
||||
+5
-35
@@ -1,15 +1,9 @@
|
||||
import eosjsecc from 'eosjs-ecc';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import { getActions, getTables } from '../utils/getFetch';
|
||||
|
||||
import * as coopService from './coop.service';
|
||||
const { verify, sha256 } = eosjsecc;
|
||||
import { getActions } from '../utils/getFetch';
|
||||
import config from '../config/config';
|
||||
import { Generator } from 'coopdoc-generator-ts';
|
||||
import type { IGenerate, IGetDocuments } from '../types';
|
||||
import type { IGenerate } from '../types';
|
||||
import { Cooperative, SovietContract } from 'cooptypes';
|
||||
import { User } from '../models';
|
||||
import { IUser } from '../models/user.model';
|
||||
|
||||
export const generator = new Generator();
|
||||
|
||||
@@ -100,8 +94,8 @@ export async function buildComplexDocument(
|
||||
|
||||
export const queryDocuments = async (
|
||||
filter: any,
|
||||
page: number = 1,
|
||||
limit: number = 100
|
||||
page = 1,
|
||||
limit = 100
|
||||
): Promise<Cooperative.Documents.IGetComplexDocuments> => {
|
||||
const actions = await getActions<SovietContract.Actions.Registry.NewResolved.INewResolved>(
|
||||
`${process.env.SIMPLE_EXPLORER_API}/get-actions`,
|
||||
@@ -117,7 +111,7 @@ export const queryDocuments = async (
|
||||
}
|
||||
);
|
||||
|
||||
let response: Cooperative.Documents.IGetComplexDocuments = {
|
||||
const response: Cooperative.Documents.IGetComplexDocuments = {
|
||||
results: [],
|
||||
page,
|
||||
limit,
|
||||
@@ -131,27 +125,3 @@ export const queryDocuments = async (
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
export const getDocuments = async (account) => {
|
||||
// let data = await getActions('/v2/history/get_actions', {
|
||||
// "filter": "soviettest1:statement",
|
||||
// "account": account,
|
||||
// })
|
||||
// for (const action of data.actions) {
|
||||
// action.decision = (await getActions('/v2/history/get_actions', {
|
||||
// "account": account,
|
||||
// "filter": "soviettest1:decision",
|
||||
// "@decision.decision_id": action.act.data.decision_id,
|
||||
// }))?.actions?.[0]
|
||||
// if (action.act.data.action == 'joincoop') {
|
||||
// try {
|
||||
// action.verified = await verify(action.act.data.statement.sign, action.act.data.statement.hash, action.act.data.statement.pkey)
|
||||
// } catch(e: any){
|
||||
// console.error(e)
|
||||
// action.verified = false
|
||||
// action.verify_message = e.message
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return data.actions
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import { createTransport } from 'nodemailer';
|
||||
import config from '../config/config';
|
||||
import logger from '../config/logger';
|
||||
|
||||
const { email, env } = config
|
||||
const { email, env } = config;
|
||||
|
||||
const transport = createTransport(email.smtp);
|
||||
|
||||
@@ -33,12 +33,14 @@ const sendEmail = async (to, subject, text) => {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const sendResetPasswordEmail = async (to, token) => {
|
||||
const subject = 'Reset password';
|
||||
// replace this url with the link to the reset password page of your front-end app
|
||||
const resetPasswordUrl = `http://link-to-app/reset-password?token=${token}`;
|
||||
const text = `Dear user,
|
||||
To reset your password, click on this link: ${resetPasswordUrl}
|
||||
If you did not request any password resets, then ignore this email.`;
|
||||
const subject = 'Восстановление доступа';
|
||||
|
||||
const resetPasswordUrl = `${config.base_url}/#/${config.coopname}/auth/reset-key?token=${token}`;
|
||||
const text = `Мы получили запрос на перевыпуск приватного ключа,
|
||||
Для перевыпуска нажмите на ссылку: ${resetPasswordUrl}. Время действия ссылки - 10 минут.
|
||||
|
||||
Если вы не запрашивали перевыпуск ключа - проигнорируйте это сообщение.`;
|
||||
|
||||
await sendEmail(to, subject, text);
|
||||
};
|
||||
|
||||
@@ -58,9 +60,4 @@ If you did not create an account, then ignore this email.`;
|
||||
await sendEmail(to, subject, text);
|
||||
};
|
||||
|
||||
export {
|
||||
transport,
|
||||
sendEmail,
|
||||
sendResetPasswordEmail,
|
||||
sendVerificationEmail,
|
||||
};
|
||||
export { transport, sendEmail, sendResetPasswordEmail, sendVerificationEmail };
|
||||
|
||||
@@ -3,7 +3,8 @@ export * as emailService from './email.service';
|
||||
export * as tokenService from './token.service';
|
||||
export * as userService from './user.service';
|
||||
export * as blockchainService from './blockchain.service';
|
||||
export * as orderService from './order.service';
|
||||
export * as dataService from './data.service';
|
||||
export * as paymentService from './payment.service';
|
||||
export * as documentService from './document.service';
|
||||
export * as coopService from './coop.service';
|
||||
export * as wsService from './ws.service';
|
||||
export * as monoService from './mono.service';
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import httpStatus from 'http-status';
|
||||
import { Mono } from '../models';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import config from '../config/config';
|
||||
import logger from '../config/logger';
|
||||
import { IHealthStatus } from '../types';
|
||||
|
||||
export const init = async (): Promise<void> => {
|
||||
const mono = await Mono.findOne({ coopname: config.coopname });
|
||||
|
||||
if (!mono)
|
||||
await Mono.create({
|
||||
coopname: config.coopname,
|
||||
status: 'install',
|
||||
});
|
||||
|
||||
logger.info('MONO инициализирован');
|
||||
};
|
||||
|
||||
export const getMonoStatus = async (): Promise<IHealthStatus> => {
|
||||
const mono = await Mono.findOne({ coopname: config.coopname });
|
||||
|
||||
if (!mono) throw new ApiError(httpStatus.BAD_REQUEST, 'Установщик не найден');
|
||||
|
||||
return mono.status;
|
||||
};
|
||||
@@ -1 +1 @@
|
||||
|
||||
export default {};
|
||||
|
||||
+25
-17
@@ -6,13 +6,27 @@ import { getUserByUsername } from './user.service';
|
||||
import * as blockchainService from './blockchain.service';
|
||||
import logger from '../config/logger';
|
||||
import { ICreateDeposit, ICreateInitialPayment } from '../types';
|
||||
import { generator } from './data.service';
|
||||
import { generator } from './document.service';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import httpStatus from 'http-status';
|
||||
import { ICreatedPayment, IYandexIPN, PaymentDetails } from '../types/common';
|
||||
import { Cooperative } from 'cooptypes';
|
||||
import { FilterQuery } from 'mongoose';
|
||||
|
||||
const { connection } = mongoose;
|
||||
|
||||
export const savePaymentMethod = async (data: Cooperative.Payments.IPaymentData) => {
|
||||
return await generator.save('paymentMethod', data);
|
||||
};
|
||||
|
||||
export const deletePaymentMethod = async (filter: FilterQuery<Cooperative.Payments.IPaymentData>) => {
|
||||
return await generator.del('paymentMethod', filter as any);
|
||||
};
|
||||
|
||||
export const listPaymentMethods = async (filter: FilterQuery<Cooperative.Payments.IPaymentData>) => {
|
||||
return await generator.list('paymentMethod', filter as any);
|
||||
};
|
||||
|
||||
function getAmountPlusFee(amount: number, provider: string): number {
|
||||
const matrix = {
|
||||
yookassa: 3.5,
|
||||
@@ -26,7 +40,7 @@ function getAmountPlusFee(amount: number, provider: string): number {
|
||||
}
|
||||
|
||||
export async function createDeposit(username: string, data: ICreateDeposit): Promise<ICreatedPayment> {
|
||||
let cooperative = await generator.constructCooperative(process.env.COOPNAME as string);
|
||||
const cooperative = await generator.constructCooperative(process.env.COOPNAME as string);
|
||||
|
||||
if (!cooperative) throw new Error('Кооператив не найден');
|
||||
|
||||
@@ -36,7 +50,7 @@ export async function createDeposit(username: string, data: ICreateDeposit): Pro
|
||||
type: 'deposit',
|
||||
data: { username, provider: data.provider, quantity: data.quantity },
|
||||
});
|
||||
const internal_id = db_order._id;
|
||||
const internal_id = db_order.id;
|
||||
|
||||
let order_id = 0;
|
||||
|
||||
@@ -104,9 +118,9 @@ export async function createDeposit(username: string, data: ICreateDeposit): Pro
|
||||
}
|
||||
|
||||
export async function createInitialOrder(username: string, data: ICreateInitialPayment): Promise<ICreatedPayment> {
|
||||
let cooperative = await generator.constructCooperative(process.env.COOPNAME as string);
|
||||
const cooperative = await generator.constructCooperative(process.env.COOPNAME as string);
|
||||
|
||||
if (!cooperative) throw new Error('Кооператив не найден');
|
||||
if (!cooperative) throw new ApiError(httpStatus.BAD_REQUEST, 'Кооператив не найден');
|
||||
|
||||
const user = await getUserByUsername(username);
|
||||
let amount = '';
|
||||
@@ -114,8 +128,6 @@ export async function createInitialOrder(username: string, data: ICreateInitialP
|
||||
if (user.type === 'individual' || user.type === 'entrepreneur') amount = cooperative.registration;
|
||||
else amount = cooperative.org_registration;
|
||||
|
||||
console.log('amount: ', cooperative);
|
||||
|
||||
const [, symbol] = cooperative.initial.split(' ');
|
||||
let order_id = 0;
|
||||
|
||||
@@ -127,7 +139,7 @@ export async function createInitialOrder(username: string, data: ICreateInitialP
|
||||
type: 'registration',
|
||||
data: { provider: data.provider, username, quantity: amount },
|
||||
});
|
||||
const internal_id = db_order._id;
|
||||
const internal_id = db_order.id;
|
||||
|
||||
if (data.provider === 'yookassa') {
|
||||
// 2. Используешь внутренний айди для получения ордера в системе платежей
|
||||
@@ -175,7 +187,7 @@ export async function createInitialOrder(username: string, data: ICreateInitialP
|
||||
type: 'registration',
|
||||
quantity: amount,
|
||||
};
|
||||
console.log('amount: ', amount);
|
||||
|
||||
order_id = await blockchainService.createOrder(order);
|
||||
|
||||
// 4. Обновляешь ордер идентификатором ордера в блокчейне (order_id)
|
||||
@@ -225,7 +237,7 @@ export async function catchIPN(ipnBody: IYandexIPN) {
|
||||
memo: '',
|
||||
});
|
||||
|
||||
console.log('Зарегистрирован новый пользователь: ', user.username);
|
||||
logger.info('Зарегистрирован новый пользователь: ', user.username);
|
||||
user.status = 'registered';
|
||||
user.is_registered = true;
|
||||
} else if (order.type === 'deposit') {
|
||||
@@ -236,7 +248,7 @@ export async function catchIPN(ipnBody: IYandexIPN) {
|
||||
memo: '',
|
||||
});
|
||||
const quantity = parseFloat(order.data.quantity);
|
||||
console.log(`Принят паевый взнос пользователя: ${user.username} на сумму ${order.data.quantity}`);
|
||||
logger.info(`Принят паевый взнос пользователя: ${user.username} на сумму ${order.data.quantity}`);
|
||||
}
|
||||
|
||||
await user.save();
|
||||
@@ -244,7 +256,7 @@ export async function catchIPN(ipnBody: IYandexIPN) {
|
||||
order.delivered = true;
|
||||
await order.save();
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
logger.warn(e);
|
||||
order.error = e;
|
||||
order.delivered = false;
|
||||
await order.save();
|
||||
@@ -269,12 +281,8 @@ export async function catchIPN(ipnBody: IYandexIPN) {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
logger.warn(`Ордер не найден: `, ipnBody);
|
||||
throw new Error(`Ордер не найден`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
createInitialOrder,
|
||||
catchIPN,
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import jwt from 'jsonwebtoken';
|
||||
import moment from 'moment';
|
||||
import httpStatus from 'http-status';
|
||||
import config from '../config/config';
|
||||
import userService from './user.service';
|
||||
import * as userService from './user.service';
|
||||
import { Token } from '../models';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import { tokenTypes } from '../config/tokens';
|
||||
@@ -113,9 +113,9 @@ export const generateResetPasswordToken = async (email) => {
|
||||
throw new ApiError(httpStatus.NOT_FOUND, 'No users found with this email');
|
||||
}
|
||||
const expires = moment().add(config.jwt.resetPasswordExpirationMinutes, 'minutes');
|
||||
const resetPasswordToken = generateToken(user.id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await saveToken(resetPasswordToken, user.id, expires, tokenTypes.RESET_PASSWORD);
|
||||
return resetPasswordToken;
|
||||
const resetKeyToken = generateToken(user.id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await saveToken(resetKeyToken, user.id, expires, tokenTypes.RESET_PASSWORD);
|
||||
return resetKeyToken;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import http from 'http-status';
|
||||
import { User } from '../models';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import { generator } from './data.service';
|
||||
import { ICreateUser, IJoinCooperative } from '../types/user.validation';
|
||||
import { generator } from './document.service';
|
||||
import { ICreateUser, IJoinCooperative } from '../types/auto-generated/user.validation';
|
||||
import ecc from 'eosjs-ecc';
|
||||
import { PublicKey, Signature } from '@wharfkit/antelope';
|
||||
import faker from 'faker';
|
||||
@@ -119,6 +119,19 @@ export const getUserByUsername = async (username: string) => {
|
||||
return user;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get user by username
|
||||
* @param {string} id
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
export const getUserById = async (_id: string) => {
|
||||
const user = await User.findById(_id);
|
||||
|
||||
if (!user) throw new ApiError(http.NOT_FOUND, 'Пользователь не найден');
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get user by email
|
||||
* @param {string} email
|
||||
@@ -134,8 +147,8 @@ export const getUserByEmail = async (email: string) => {
|
||||
* @param {Object} updateBody
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
export const updateUserById = async (username, updateBody) => {
|
||||
const user = await getUserByUsername(username);
|
||||
export const updateUserById = async (id, updateBody) => {
|
||||
const user = await getUserById(id);
|
||||
if (!user) {
|
||||
throw new ApiError(http.NOT_FOUND, 'Пользователь не найден');
|
||||
}
|
||||
@@ -147,6 +160,27 @@ export const updateUserById = async (username, updateBody) => {
|
||||
return user;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update user by username
|
||||
* @param {string} username
|
||||
* @param {Object} updateBody
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
export const updateUserByUsername = async (username, updateBody) => {
|
||||
const user = await getUserByUsername(username);
|
||||
|
||||
if (!user) {
|
||||
throw new ApiError(http.NOT_FOUND, 'Пользователь не найден');
|
||||
}
|
||||
if (updateBody.email && (await User.isEmailTaken(updateBody.email))) {
|
||||
throw new ApiError(http.BAD_REQUEST, 'Email already taken');
|
||||
}
|
||||
Object.assign(user, updateBody);
|
||||
|
||||
await user.save();
|
||||
return user;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete user by id
|
||||
* @param {string} username
|
||||
@@ -157,16 +191,6 @@ export const deleteUserById = async (username) => {
|
||||
if (!user) {
|
||||
throw new ApiError(http.NOT_FOUND, 'Пользователь не найден');
|
||||
}
|
||||
await user.remove();
|
||||
await user.deleteOne();
|
||||
return user;
|
||||
};
|
||||
|
||||
export default {
|
||||
createUser,
|
||||
queryUsers,
|
||||
getUserByUsername,
|
||||
getUserByEmail,
|
||||
updateUserById,
|
||||
deleteUserById,
|
||||
createServiceUser,
|
||||
};
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* This file was automatically generated by joi-to-typescript
|
||||
* Do not modify this file manually
|
||||
*/
|
||||
|
||||
export interface forgotPassword {
|
||||
body?: {
|
||||
email: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface login {
|
||||
body?: {
|
||||
password: string;
|
||||
username: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface logout {
|
||||
body?: {
|
||||
refreshToken: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface refreshTokens {
|
||||
body?: {
|
||||
refreshToken: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface resetPassword {
|
||||
body?: {
|
||||
password: string;
|
||||
};
|
||||
query?: {
|
||||
token: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface verifyEmail {
|
||||
query?: {
|
||||
token: string;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* This file was automatically generated by joi-to-typescript
|
||||
* Do not modify this file manually
|
||||
*/
|
||||
|
||||
export interface IForgotPassword {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface ILogin {
|
||||
email: string;
|
||||
now: string;
|
||||
signature: string;
|
||||
}
|
||||
|
||||
export interface ILogout {
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export interface IRefreshTokens {
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export interface IResetKeyBody {
|
||||
public_key: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface IVerifyEmail {
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface RForgotKey {
|
||||
body: {
|
||||
email: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RLogin {
|
||||
body: {
|
||||
email: string;
|
||||
now: string;
|
||||
signature: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RLogout {
|
||||
body: {
|
||||
refreshToken: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RRefreshTokens {
|
||||
body: {
|
||||
refreshToken: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RResetKey {
|
||||
body: {
|
||||
public_key: string;
|
||||
token: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RVerifyEmail {
|
||||
query: {
|
||||
token: string;
|
||||
};
|
||||
}
|
||||
+8
@@ -21,6 +21,10 @@ export interface IGenerate {
|
||||
}
|
||||
|
||||
export interface IGetDocuments {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
filter?: {
|
||||
receiver?: string;
|
||||
};
|
||||
@@ -50,6 +54,10 @@ export interface RGenerate {
|
||||
|
||||
export interface RGetDocuments {
|
||||
query?: {
|
||||
/**
|
||||
* Unknown Property
|
||||
*/
|
||||
[x: string]: unknown;
|
||||
filter?: {
|
||||
receiver?: string;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* This file was automatically generated by joi-to-typescript
|
||||
* Do not modify this file manually
|
||||
*/
|
||||
|
||||
export * from './auth.validation';
|
||||
export * from './coop.validation';
|
||||
export * from './document.validation';
|
||||
export * from './notify.validation';
|
||||
export * from './payment.validation';
|
||||
export * from './user.validation';
|
||||
+78
@@ -3,6 +3,18 @@
|
||||
* Do not modify this file manually
|
||||
*/
|
||||
|
||||
export interface IBankAccount {
|
||||
account_number: string;
|
||||
bank_name: string;
|
||||
card_number?: string;
|
||||
currency: 'RUB' | 'Other';
|
||||
details: {
|
||||
bik: string;
|
||||
corr: string;
|
||||
kpp: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ICreateDeposit {
|
||||
provider: 'yookassa';
|
||||
quantity: string;
|
||||
@@ -12,6 +24,10 @@ export interface ICreateInitialPayment {
|
||||
provider: 'yookassa';
|
||||
}
|
||||
|
||||
export interface IDeletePaymentMethod {
|
||||
method_id: number;
|
||||
}
|
||||
|
||||
export interface IRecieveIPN {
|
||||
event: string;
|
||||
object: {
|
||||
@@ -98,3 +114,65 @@ export interface IRecieveIPN {
|
||||
};
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface ISavePaymentMethod {
|
||||
data: {
|
||||
phone: string;
|
||||
} | ({
|
||||
account_number: string;
|
||||
bank_name: string;
|
||||
card_number?: string;
|
||||
currency: 'RUB' | 'Other';
|
||||
details: {
|
||||
bik: string;
|
||||
corr: string;
|
||||
kpp: string;
|
||||
};
|
||||
});
|
||||
method_id: number;
|
||||
method_type: 'sbp' | 'bank_transfer';
|
||||
username: string;
|
||||
}
|
||||
|
||||
export interface ISbpDetails {
|
||||
phone: string;
|
||||
}
|
||||
|
||||
export interface RDeletePaymentMethod {
|
||||
body: {
|
||||
method_id: number;
|
||||
};
|
||||
params?: {
|
||||
username: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RGetListPaymentMethods {
|
||||
params?: {
|
||||
username?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RSavePaymentMethod {
|
||||
body: {
|
||||
data: {
|
||||
phone: string;
|
||||
} | ({
|
||||
account_number: string;
|
||||
bank_name: string;
|
||||
card_number?: string;
|
||||
currency: 'RUB' | 'Other';
|
||||
details: {
|
||||
bik: string;
|
||||
corr: string;
|
||||
kpp: string;
|
||||
};
|
||||
});
|
||||
method_id: number;
|
||||
method_type: 'sbp' | 'bank_transfer';
|
||||
username: string;
|
||||
};
|
||||
params?: {
|
||||
username: string;
|
||||
};
|
||||
}
|
||||
+3
-3
@@ -76,7 +76,7 @@ export interface ICreateUser {
|
||||
password: string;
|
||||
public_key: string;
|
||||
referer?: string;
|
||||
role: 'user' | 'admin';
|
||||
role: 'user' | 'chairman' | 'member';
|
||||
type: 'individual' | 'entrepreneur' | 'organization';
|
||||
username: string;
|
||||
}
|
||||
@@ -243,7 +243,7 @@ export interface RCreateUser {
|
||||
password: string;
|
||||
public_key: string;
|
||||
referer?: string;
|
||||
role: 'user' | 'admin';
|
||||
role: 'user' | 'chairman' | 'member';
|
||||
type: 'individual' | 'entrepreneur' | 'organization';
|
||||
username: string;
|
||||
};
|
||||
@@ -283,6 +283,6 @@ export interface RUpdateUser {
|
||||
password?: string;
|
||||
};
|
||||
params?: {
|
||||
username: any;
|
||||
username: string;
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IEntrepreneurData, IGeneratedDocument, IIndividualData, IOrganizationData } from "coopdoc-generator-ts"
|
||||
import { SovietContract } from "cooptypes";
|
||||
import { IEntrepreneurData, IGeneratedDocument, IIndividualData, IOrganizationData } from 'coopdoc-generator-ts';
|
||||
import { Cooperative, SovietContract } from 'cooptypes';
|
||||
|
||||
// Определение унифицированного типа для деталей платежа
|
||||
export interface PaymentDetails {
|
||||
@@ -8,73 +8,69 @@ export interface PaymentDetails {
|
||||
}
|
||||
|
||||
export interface IYandexIPN {
|
||||
type: string;
|
||||
event: string;
|
||||
object: {
|
||||
id: string;
|
||||
status: string;
|
||||
paid: boolean;
|
||||
amount: {
|
||||
value: string;
|
||||
currency: string;
|
||||
};
|
||||
income_amount: {
|
||||
value: string;
|
||||
currency: string;
|
||||
};
|
||||
authorization_details: {
|
||||
rrn: string;
|
||||
auth_code: string;
|
||||
three_d_secure: {
|
||||
applied: boolean;
|
||||
};
|
||||
};
|
||||
created_at: string;
|
||||
description: string;
|
||||
expires_at: string;
|
||||
metadata: Record<string, unknown>;
|
||||
payment_method: {
|
||||
type: string;
|
||||
id: string;
|
||||
saved: boolean;
|
||||
card: {
|
||||
first6: string;
|
||||
last4: string;
|
||||
expiry_month: string;
|
||||
expiry_year: string;
|
||||
card_type: string;
|
||||
issuer_country: string;
|
||||
issuer_name: string;
|
||||
};
|
||||
title: string;
|
||||
};
|
||||
refundable: boolean;
|
||||
test: boolean;
|
||||
type: string;
|
||||
event: string;
|
||||
object: {
|
||||
id: string;
|
||||
status: string;
|
||||
paid: boolean;
|
||||
amount: {
|
||||
value: string;
|
||||
currency: string;
|
||||
};
|
||||
income_amount: {
|
||||
value: string;
|
||||
currency: string;
|
||||
};
|
||||
authorization_details: {
|
||||
rrn: string;
|
||||
auth_code: string;
|
||||
three_d_secure: {
|
||||
applied: boolean;
|
||||
};
|
||||
};
|
||||
created_at: string;
|
||||
description: string;
|
||||
expires_at: string;
|
||||
metadata: Record<string, unknown>;
|
||||
payment_method: {
|
||||
type: string;
|
||||
id: string;
|
||||
saved: boolean;
|
||||
card: {
|
||||
first6: string;
|
||||
last4: string;
|
||||
expiry_month: string;
|
||||
expiry_year: string;
|
||||
card_type: string;
|
||||
issuer_country: string;
|
||||
issuer_name: string;
|
||||
};
|
||||
title: string;
|
||||
};
|
||||
refundable: boolean;
|
||||
test: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ICreatedPayment {
|
||||
provider: string
|
||||
provider: string;
|
||||
order_id: string | number;
|
||||
details: PaymentDetails
|
||||
};
|
||||
|
||||
export interface IGetResponse {
|
||||
results: any[]
|
||||
page: number
|
||||
limit: number
|
||||
details: PaymentDetails;
|
||||
}
|
||||
|
||||
export type IGetResponse<T> = Cooperative.Documents.IGetResponse<T>;
|
||||
|
||||
export interface IGetActions<T> {
|
||||
results: IAction[]
|
||||
page: number
|
||||
limit: number
|
||||
results: IAction[];
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface IGetTables<T> {
|
||||
results: ITable[]
|
||||
page: number
|
||||
limit: number
|
||||
results: ITable[];
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface ITable {
|
||||
@@ -89,25 +85,24 @@ export interface ITable {
|
||||
value?: any;
|
||||
}
|
||||
|
||||
|
||||
export interface IAction {
|
||||
transaction_id: string;
|
||||
account: string;
|
||||
block_num: number,
|
||||
block_id: string,
|
||||
chain_id: string,
|
||||
block_num: number;
|
||||
block_id: string;
|
||||
chain_id: string;
|
||||
name: string;
|
||||
receiver: string;
|
||||
authorization: Array<{
|
||||
actor: string;
|
||||
permission: string;
|
||||
actor: string;
|
||||
permission: string;
|
||||
}>;
|
||||
data: any;
|
||||
action_ordinal: number;
|
||||
global_sequence: string;
|
||||
account_ram_deltas: Array<{
|
||||
account: string;
|
||||
delta: number;
|
||||
account: string;
|
||||
delta: number;
|
||||
}>;
|
||||
console: string;
|
||||
receipt: {
|
||||
@@ -116,8 +111,8 @@ export interface IAction {
|
||||
global_sequence: string;
|
||||
recv_sequence: string;
|
||||
auth_sequence: Array<{
|
||||
account: string;
|
||||
sequence: string;
|
||||
account: string;
|
||||
sequence: string;
|
||||
}>;
|
||||
code_sequence: number;
|
||||
abi_sequence: number;
|
||||
@@ -127,51 +122,46 @@ export interface IAction {
|
||||
elapsed: number;
|
||||
}
|
||||
|
||||
export interface IExtendedTable extends ITable {
|
||||
|
||||
}
|
||||
export type IExtendedTable = ITable;
|
||||
|
||||
export interface IExtendedAction extends IAction {
|
||||
user: IIndividualData | IEntrepreneurData | IOrganizationData | null;
|
||||
}
|
||||
|
||||
|
||||
export interface IComplexStatement {
|
||||
action: IExtendedAction
|
||||
document: IGeneratedDocument
|
||||
action: IExtendedAction;
|
||||
document: IGeneratedDocument;
|
||||
}
|
||||
|
||||
export interface IComplexDecision {
|
||||
action: IExtendedAction
|
||||
document: IGeneratedDocument
|
||||
votes_for: IExtendedAction[],
|
||||
votes_against: IExtendedAction[],
|
||||
action: IExtendedAction;
|
||||
document: IGeneratedDocument;
|
||||
votes_for: IExtendedAction[];
|
||||
votes_against: IExtendedAction[];
|
||||
}
|
||||
|
||||
export interface IComplexAct {
|
||||
action?: IExtendedAction
|
||||
document?: IGeneratedDocument
|
||||
action?: IExtendedAction;
|
||||
document?: IGeneratedDocument;
|
||||
}
|
||||
|
||||
export interface IComplexDocument {
|
||||
statement: IComplexStatement
|
||||
decision: IComplexDecision
|
||||
acts: IComplexAct[]
|
||||
statement: IComplexStatement;
|
||||
decision: IComplexDecision;
|
||||
acts: IComplexAct[];
|
||||
}
|
||||
|
||||
|
||||
export interface IGetComplexDocuments {
|
||||
results: IComplexDocument[]
|
||||
page: number
|
||||
limit: number
|
||||
results: IComplexDocument[];
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
|
||||
export interface IAgenda {
|
||||
row: SovietContract.Tables.Decisions.IDecision
|
||||
action: IAction
|
||||
row: SovietContract.Tables.Decisions.IDecision;
|
||||
action: IAction;
|
||||
}
|
||||
|
||||
export interface IComplexAgenda extends IAgenda {
|
||||
document: IComplexDocument
|
||||
document: IComplexDocument;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
/**
|
||||
* This file was automatically generated by joi-to-typescript
|
||||
* Do not modify this file manually
|
||||
*/
|
||||
|
||||
export * from './auth.validation';
|
||||
export * from './coop.validation';
|
||||
export * from './data.validation';
|
||||
export * from './notify.validation';
|
||||
export * from './order.validation';
|
||||
export * from './user.validation';
|
||||
export * from './auto-generated';
|
||||
export * from './mono.types';
|
||||
export * from './common';
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { GetInfoResult } from 'eosjs/dist/eosjs-rpc-interfaces';
|
||||
|
||||
export type IHealthStatus = 'install' | 'active' | 'maintenance';
|
||||
|
||||
export interface IHealthResponse {
|
||||
status: IHealthStatus;
|
||||
blockchain: GetInfoResult;
|
||||
}
|
||||
Vendored
+28
@@ -0,0 +1,28 @@
|
||||
import 'mongoose';
|
||||
|
||||
declare module 'mongoose' {
|
||||
interface PaginateOptions {
|
||||
sortBy?: string;
|
||||
populate?: string;
|
||||
limit?: number;
|
||||
page?: number;
|
||||
}
|
||||
|
||||
interface QueryResult<T> {
|
||||
results: T[];
|
||||
page: number;
|
||||
limit: number;
|
||||
totalPages: number;
|
||||
totalResults: number;
|
||||
}
|
||||
|
||||
interface PaginateModel<T extends Document> extends Model<T> {
|
||||
paginate(filter: FilterQuery<T>, options: PaginateOptions): Promise<QueryResult<T>>;
|
||||
}
|
||||
}
|
||||
declare global {
|
||||
interface Error {
|
||||
statusCode?: number;
|
||||
subcode?: number;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,56 @@
|
||||
import * as Joi from 'joi';
|
||||
|
||||
import { password } from './custom.validation'
|
||||
import { password } from './custom.validation';
|
||||
|
||||
export const login = Joi.object({
|
||||
body: Joi.object().keys({
|
||||
username: Joi.string().required(),
|
||||
password: Joi.string().required(),
|
||||
}),
|
||||
})
|
||||
export const IRefreshTokens = Joi.object({
|
||||
refreshToken: Joi.string().required(),
|
||||
});
|
||||
|
||||
export const logout = Joi.object({
|
||||
body: Joi.object().keys({
|
||||
refreshToken: Joi.string().required(),
|
||||
}),
|
||||
})
|
||||
export const RRefreshTokens = Joi.object({
|
||||
body: IRefreshTokens.required(),
|
||||
});
|
||||
|
||||
export const refreshTokens = Joi.object({
|
||||
body: Joi.object().keys({
|
||||
refreshToken: Joi.string().required(),
|
||||
}),
|
||||
})
|
||||
export const IForgotPassword = Joi.object({
|
||||
email: Joi.string().email().required(),
|
||||
});
|
||||
|
||||
export const forgotPassword = Joi.object({
|
||||
body: Joi.object().keys({
|
||||
email: Joi.string().email().required(),
|
||||
}),
|
||||
})
|
||||
export const RForgotKey = Joi.object({
|
||||
body: IForgotPassword.required(),
|
||||
});
|
||||
|
||||
export const resetPassword = Joi.object({
|
||||
query: Joi.object().keys({
|
||||
token: Joi.string().required(),
|
||||
}),
|
||||
body: Joi.object().keys({
|
||||
password: Joi.string().required().custom(password),
|
||||
}),
|
||||
})
|
||||
// Внутренние параметры
|
||||
export const ILogin = Joi.object({
|
||||
signature: Joi.string().required(),
|
||||
email: Joi.string().email().required(),
|
||||
now: Joi.string().required(),
|
||||
});
|
||||
|
||||
export const verifyEmail = Joi.object({
|
||||
query: Joi.object().keys({
|
||||
token: Joi.string().required(),
|
||||
}),
|
||||
})
|
||||
export const ILogout = Joi.object({
|
||||
refreshToken: Joi.string().required(),
|
||||
});
|
||||
|
||||
export const IResetKeyBody = Joi.object({
|
||||
public_key: Joi.string().required(),
|
||||
token: Joi.string().required(),
|
||||
});
|
||||
|
||||
export const IVerifyEmail = Joi.object({
|
||||
token: Joi.string().required(),
|
||||
});
|
||||
|
||||
// Внешние запросы
|
||||
export const RLogin = Joi.object({
|
||||
body: ILogin.required(),
|
||||
});
|
||||
|
||||
export const RLogout = Joi.object({
|
||||
body: ILogout.required(),
|
||||
});
|
||||
|
||||
export const RResetKey = Joi.object({
|
||||
body: IResetKeyBody.required(),
|
||||
});
|
||||
|
||||
export const RVerifyEmail = Joi.object({
|
||||
query: IVerifyEmail.required(),
|
||||
});
|
||||
|
||||
+6
-6
@@ -14,7 +14,7 @@ export const IGenerate = Joi.object({
|
||||
}).unknown(true);
|
||||
|
||||
export const RGenerate = Joi.object({
|
||||
body: IGenerate.required()
|
||||
body: IGenerate.required(),
|
||||
}).required();
|
||||
|
||||
export const IGetDocuments = Joi.object({
|
||||
@@ -24,14 +24,14 @@ export const IGetDocuments = Joi.object({
|
||||
sortBy: Joi.string(),
|
||||
limit: Joi.number().integer(),
|
||||
page: Joi.number().integer(),
|
||||
})
|
||||
}).unknown(true);
|
||||
|
||||
export const RGetDocuments = Joi.object({
|
||||
query: IGetDocuments
|
||||
})
|
||||
query: IGetDocuments,
|
||||
});
|
||||
|
||||
export const RGetMyDocuments = Joi.object({
|
||||
query: Joi.object().keys({
|
||||
username: Joi.string()
|
||||
username: Joi.string(),
|
||||
}),
|
||||
})
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
export * as authValidation from './auth.validation';
|
||||
export * as userValidation from './user.validation';
|
||||
export * as documentValidation from './data.validation';
|
||||
export * as orderValidation from './order.validation';
|
||||
export * as documentValidation from './document.validation';
|
||||
export * as orderValidation from './payment.validation';
|
||||
export * as coopValidation from './coop.validation';
|
||||
export * as customValidation from './custom.validation';
|
||||
export * as notifyValidation from './notify.validation';
|
||||
|
||||
+74
-14
@@ -2,21 +2,20 @@ import * as Joi from 'joi';
|
||||
|
||||
export const ICreateDeposit = Joi.object().keys({
|
||||
quantity: Joi.string().required(),
|
||||
provider: Joi.string().valid('yookassa').required()
|
||||
})
|
||||
provider: Joi.string().valid('yookassa').required(),
|
||||
});
|
||||
|
||||
export const ICreateInitialPayment = Joi.object().keys({
|
||||
provider: Joi.string().valid('yookassa').required()
|
||||
})
|
||||
provider: Joi.string().valid('yookassa').required(),
|
||||
});
|
||||
|
||||
export const RCreateDeposit = {
|
||||
body: ICreateDeposit.required(),
|
||||
}
|
||||
};
|
||||
|
||||
export const RCreateInitialPayment = {
|
||||
body: ICreateInitialPayment.required(),
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export const IRecieveIPN = Joi.object({
|
||||
type: Joi.string().required(),
|
||||
@@ -28,15 +27,21 @@ export const IRecieveIPN = Joi.object({
|
||||
amount: Joi.object({
|
||||
value: Joi.string().required(),
|
||||
currency: Joi.string().required(),
|
||||
}).required().unknown(true),
|
||||
})
|
||||
.required()
|
||||
.unknown(true),
|
||||
income_amount: Joi.object({
|
||||
value: Joi.string().required(),
|
||||
currency: Joi.string().required(),
|
||||
}).required().unknown(true),
|
||||
})
|
||||
.required()
|
||||
.unknown(true),
|
||||
refunded_amount: Joi.object({
|
||||
value: Joi.string().required(),
|
||||
currency: Joi.string().required(),
|
||||
}).required().unknown(true),
|
||||
})
|
||||
.required()
|
||||
.unknown(true),
|
||||
authorization_details: Joi.object({
|
||||
rrn: Joi.string(),
|
||||
auth_code: Joi.string(),
|
||||
@@ -62,15 +67,70 @@ export const IRecieveIPN = Joi.object({
|
||||
issuer_name: Joi.string(),
|
||||
}).unknown(true),
|
||||
title: Joi.string(),
|
||||
}).required().unknown(true),
|
||||
})
|
||||
.required()
|
||||
.unknown(true),
|
||||
refundable: Joi.boolean(),
|
||||
test: Joi.boolean(),
|
||||
}).required().unknown(true),
|
||||
})
|
||||
.required()
|
||||
.unknown(true),
|
||||
});
|
||||
|
||||
export const RRecieveIPN = {
|
||||
body: IRecieveIPN.required()
|
||||
}
|
||||
body: IRecieveIPN.required(),
|
||||
};
|
||||
|
||||
const RussianBankDetailsSchema = Joi.object({
|
||||
bik: Joi.string().required(),
|
||||
corr: Joi.string().required(),
|
||||
kpp: Joi.string().required(),
|
||||
});
|
||||
|
||||
export const IBankAccount = Joi.object({
|
||||
currency: Joi.string().required().valid('RUB', 'Other'),
|
||||
card_number: Joi.string().default(''),
|
||||
bank_name: Joi.string().required(),
|
||||
account_number: Joi.string().required(),
|
||||
details: RussianBankDetailsSchema.required(),
|
||||
});
|
||||
|
||||
export const ISbpDetails = Joi.object({
|
||||
phone: Joi.string().required(),
|
||||
});
|
||||
|
||||
export const RGetListPaymentMethods = Joi.object({
|
||||
params: {
|
||||
username: Joi.string().optional(),
|
||||
},
|
||||
});
|
||||
|
||||
const MethodTypes = Joi.string().valid('sbp', 'bank_transfer');
|
||||
|
||||
export const ISavePaymentMethod = Joi.object({
|
||||
username: Joi.string().required(),
|
||||
method_id: Joi.number().required(),
|
||||
method_type: MethodTypes.required(),
|
||||
data: Joi.alternatives().try(ISbpDetails, IBankAccount).required(),
|
||||
});
|
||||
|
||||
export const RSavePaymentMethod = Joi.object({
|
||||
params: {
|
||||
username: Joi.string().required(),
|
||||
},
|
||||
body: ISavePaymentMethod.required(),
|
||||
});
|
||||
|
||||
export const IDeletePaymentMethod = Joi.object({
|
||||
method_id: Joi.number().required(),
|
||||
});
|
||||
|
||||
export const RDeletePaymentMethod = Joi.object({
|
||||
params: {
|
||||
username: Joi.string().required(),
|
||||
},
|
||||
body: IDeletePaymentMethod.required(),
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as Joi from 'joi';
|
||||
import { password, objectId } from './custom.validation';
|
||||
import { IBankAccount } from './payment.validation';
|
||||
|
||||
export const IIndividualData = Joi.object({
|
||||
first_name: Joi.string().required(),
|
||||
@@ -24,20 +25,6 @@ const RussiaDetailsSchema = Joi.object({
|
||||
ogrn: Joi.string().required(),
|
||||
});
|
||||
|
||||
const RussianBankDetailsSchema = Joi.object({
|
||||
bik: Joi.string().required(),
|
||||
corr: Joi.string().required(),
|
||||
kpp: Joi.string().required(),
|
||||
});
|
||||
|
||||
const IBankAccount = Joi.object({
|
||||
currency: Joi.string().required().valid('RUB', 'Other'),
|
||||
card_number: Joi.string().default(''),
|
||||
bank_name: Joi.string().required(),
|
||||
account_number: Joi.string().required(),
|
||||
details: RussianBankDetailsSchema.required(),
|
||||
});
|
||||
|
||||
export const IOrganizationData = Joi.object({
|
||||
type: Joi.string().valid('coop', 'ooo', 'oao', 'zao', 'pao', 'ao').required(),
|
||||
is_cooperative: Joi.boolean().required().default(false),
|
||||
@@ -71,7 +58,7 @@ export const IEntrepreneurData = Joi.object({
|
||||
export const ICreateUser = Joi.object({
|
||||
email: Joi.string().required().email(),
|
||||
password: Joi.string().required().custom(password),
|
||||
role: Joi.string().required().valid('user', 'admin'),
|
||||
role: Joi.string().required().valid('user', 'chairman', 'member'),
|
||||
public_key: Joi.string().required(),
|
||||
username: Joi.string().required().length(12),
|
||||
referer: Joi.string().length(12).allow('').optional(),
|
||||
@@ -112,7 +99,7 @@ export const RGetUser = Joi.object({
|
||||
|
||||
export const RUpdateUser = Joi.object({
|
||||
params: Joi.object().keys({
|
||||
username: Joi.required().custom(objectId),
|
||||
username: Joi.string().required(),
|
||||
}),
|
||||
body: Joi.object()
|
||||
.keys({
|
||||
|
||||
+36
-42
@@ -1,7 +1,7 @@
|
||||
import { SovietContract, RegistratorContract } from 'cooptypes';
|
||||
import { SovietContract, RegistratorContract, DraftContract } from 'cooptypes';
|
||||
import { IAction, ITable } from '../../src/types/common';
|
||||
import mongoose from 'mongoose';
|
||||
import { Cooperative } from 'cooptypes';
|
||||
import { DocumentsRegistry } from 'coopdoc-generator-ts';
|
||||
|
||||
export const insertAction = async (action: IAction) => {
|
||||
const collection = mongoose.connection.db.collection('actions'); // Замените на имя вашей коллекции
|
||||
@@ -18,43 +18,6 @@ export const insertDelta = async (delta: ITable) => {
|
||||
await collection.insertOne(delta);
|
||||
};
|
||||
|
||||
export const fixtureVoskhod = (): Cooperative.Users.IOrganizationData => {
|
||||
return {
|
||||
username: 'voskhod',
|
||||
type: 'coop',
|
||||
is_cooperative: true,
|
||||
short_name: 'Voskhod',
|
||||
full_name: 'ПК ВОСХОД',
|
||||
represented_by: {
|
||||
first_name: 'Алексей',
|
||||
last_name: 'Муравьев',
|
||||
middle_name: 'Николаевич',
|
||||
position: 'Председатель',
|
||||
based_on: 'Решения общего собрания №1',
|
||||
},
|
||||
country: 'Russia',
|
||||
city: 'Moscow',
|
||||
full_address: '123 Main St, Moscow, Russia',
|
||||
email: 'contact@orgco.com',
|
||||
phone: '+71234567890',
|
||||
details: {
|
||||
inn: '1234567890',
|
||||
ogrn: '1234567890123',
|
||||
},
|
||||
bank_account: {
|
||||
account_number: '40817810099910004312',
|
||||
currency: 'RUB',
|
||||
card_number: '1234567890123456',
|
||||
bank_name: 'Sberbank',
|
||||
details: {
|
||||
bik: '123456789',
|
||||
corr: '30101810400000000225',
|
||||
kpp: '123456789',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const installInitialCooperativeData = async () => {
|
||||
const delta1 = fixtureDelta(0, 'registrator', 'registrator', 'orgs', '1', {
|
||||
username: 'voskhod',
|
||||
@@ -64,9 +27,12 @@ export const installInitialCooperativeData = async () => {
|
||||
is_cooperative: true,
|
||||
is_branched: false,
|
||||
coop_type: 'conscoop',
|
||||
registration: '2.0000 RUB',
|
||||
initial: '1.0000 RUB',
|
||||
minimum: '1.0000 RUB',
|
||||
registration: '200.0000 TRUB',
|
||||
initial: '100.0000 TRUB',
|
||||
minimum: '100.0000 TRUB',
|
||||
org_registration: '2000.0000 TRUB',
|
||||
org_initial: '1000.0000 TRUB',
|
||||
org_minimum: '1000.0000 TRUB',
|
||||
} as RegistratorContract.Tables.Cooperatives.ICooperative);
|
||||
|
||||
await insertDelta(delta1);
|
||||
@@ -89,6 +55,34 @@ export const installInitialCooperativeData = async () => {
|
||||
} as SovietContract.Tables.Boards.IBoards);
|
||||
|
||||
await insertDelta(delta2);
|
||||
const k = 1;
|
||||
|
||||
for (const id in DocumentsRegistry) {
|
||||
const template = DocumentsRegistry[id as unknown as keyof typeof DocumentsRegistry];
|
||||
|
||||
await insertDelta(
|
||||
fixtureDelta(0, 'draft', 'draft', 'drafts', String(k), {
|
||||
id: String(k),
|
||||
creator: 'eosio',
|
||||
version: String(1),
|
||||
default_translation_id: String(k),
|
||||
registry_id: String(id),
|
||||
title: template.title,
|
||||
description: template.description,
|
||||
context: template.context,
|
||||
model: JSON.stringify(template.model),
|
||||
} as DraftContract.Tables.Drafts.IDraft)
|
||||
);
|
||||
|
||||
await insertDelta(
|
||||
fixtureDelta(0, 'draft', 'draft', 'translations', String(k), {
|
||||
id: String(k),
|
||||
draft_id: String(k),
|
||||
lang: 'ru',
|
||||
data: JSON.stringify(template.translations.ru),
|
||||
} as DraftContract.Tables.Translations.ITranslation)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const fixtureDelta = (
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import mongoose from 'mongoose';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import faker from 'faker';
|
||||
import User from '../../src/models/user.model';
|
||||
import { generateUsername } from '../utils/generateUsername';
|
||||
import { Cooperative } from 'cooptypes';
|
||||
import { ICreateUser } from '../../src/types';
|
||||
|
||||
const password = 'password1';
|
||||
const email1 = faker.internet.email().toLowerCase();
|
||||
|
||||
export const participantOne: ICreateUser = {
|
||||
email: email1,
|
||||
password,
|
||||
username: generateUsername(),
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
role: 'user',
|
||||
referer: '',
|
||||
type: 'individual',
|
||||
individual_data: {
|
||||
first_name: faker.name.firstName(),
|
||||
last_name: faker.name.lastName(),
|
||||
middle_name: '',
|
||||
birthdate: '2023-04-01',
|
||||
phone: '+1234567890',
|
||||
email: email1,
|
||||
full_address: 'Russia, Moscow, Tverskaya street, 1',
|
||||
},
|
||||
};
|
||||
+3
-1
@@ -2,8 +2,10 @@ import moment from 'moment';
|
||||
import config from '../../src/config/config';
|
||||
import { tokenTypes } from '../../src/config/tokens';
|
||||
import * as tokenService from '../../src/services/token.service';
|
||||
import { userOne, admin } from './user.fixture';
|
||||
import { userOne, userTwo, admin } from './user.fixture';
|
||||
|
||||
export const accessTokenExpires = moment().add(config.jwt.accessExpirationMinutes, 'minutes');
|
||||
export const userOneAccessToken = tokenService.generateToken(userOne._id, accessTokenExpires, tokenTypes.ACCESS);
|
||||
export const userTwoAccessToken = tokenService.generateToken(userTwo._id, accessTokenExpires, tokenTypes.ACCESS);
|
||||
|
||||
export const adminAccessToken = tokenService.generateToken(admin._id, accessTokenExpires, tokenTypes.ACCESS);
|
||||
|
||||
+247
-28
@@ -1,85 +1,304 @@
|
||||
import mongoose from 'mongoose';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import faker from 'faker';
|
||||
import User from '../../src/models/user.model';
|
||||
import User, { IUser } from '../../src/models/user.model';
|
||||
import { generateUsername } from '../utils/generateUsername';
|
||||
import { Cooperative } from 'cooptypes';
|
||||
import { ICreateUser } from '../../src/types';
|
||||
import { ObjectId } from 'mongodb';
|
||||
|
||||
const password = 'password1';
|
||||
const salt = bcrypt.genSaltSync(8);
|
||||
const hashedPassword = bcrypt.hashSync(password, salt);
|
||||
const email1 = faker.internet.email().toLowerCase();
|
||||
|
||||
export const userOne = {
|
||||
_id: mongoose.Types.ObjectId(),
|
||||
const generateRandomId = () => new mongoose.Types.ObjectId();
|
||||
|
||||
type testUser = Omit<IUser, 'getPrivateData' | 'isPasswordMatch' | 'private_data'> & {
|
||||
_id: mongoose.Types.ObjectId;
|
||||
individual_data?: Cooperative.Users.IIndividualData;
|
||||
organization_data?: Cooperative.Users.IOrganizationData;
|
||||
entrepreneur_data?: Cooperative.Users.IEntrepreneurData;
|
||||
block_num: number;
|
||||
deleted: boolean;
|
||||
};
|
||||
|
||||
const email1 = faker.internet.email().toLowerCase();
|
||||
const email2 = faker.internet.email().toLowerCase();
|
||||
const email3 = faker.internet.email().toLowerCase();
|
||||
const email4 = faker.internet.email().toLowerCase();
|
||||
const email5 = faker.internet.email().toLowerCase();
|
||||
|
||||
const adminUsername = generateUsername();
|
||||
|
||||
export const admin: testUser = {
|
||||
_id: generateRandomId(),
|
||||
email: email1,
|
||||
password,
|
||||
username: generateUsername(),
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
role: 'user',
|
||||
status: 'active',
|
||||
has_account: false,
|
||||
message: '',
|
||||
is_registered: true,
|
||||
role: 'chairman',
|
||||
is_email_verified: false,
|
||||
username: adminUsername,
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
statement: {
|
||||
hash: '',
|
||||
meta: {},
|
||||
public_key: '',
|
||||
signature: '',
|
||||
},
|
||||
referer: '',
|
||||
type: 'individual',
|
||||
individual_data: {
|
||||
username: adminUsername,
|
||||
first_name: faker.name.firstName(),
|
||||
last_name: faker.name.lastName(),
|
||||
middle_name: '',
|
||||
birthdate: '2023-04-01',
|
||||
phone: '+1234567890',
|
||||
email: email1,
|
||||
full_address: 'Russia, Moscow, Tverskaya street, 1',
|
||||
full_address: 'Russia, Moscow, Tverskaya street, 3',
|
||||
},
|
||||
block_num: 0,
|
||||
deleted: false,
|
||||
};
|
||||
const email2 = faker.internet.email().toLowerCase();
|
||||
|
||||
export const userTwo = {
|
||||
_id: mongoose.Types.ObjectId(),
|
||||
name: faker.name.findName(),
|
||||
const usernameOne = generateUsername();
|
||||
export const userOne: testUser = {
|
||||
_id: generateRandomId(),
|
||||
email: email2,
|
||||
signature: '-',
|
||||
signature_hash: '-',
|
||||
username: generateUsername(),
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
password,
|
||||
has_account: false,
|
||||
status: 'active',
|
||||
message: '',
|
||||
is_registered: true,
|
||||
role: 'user',
|
||||
is_email_verified: false,
|
||||
username: usernameOne,
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
statement: {
|
||||
hash: '',
|
||||
meta: {},
|
||||
public_key: '',
|
||||
signature: '',
|
||||
},
|
||||
referer: '',
|
||||
type: 'individual',
|
||||
individual_data: {
|
||||
username: usernameOne,
|
||||
first_name: faker.name.firstName(),
|
||||
last_name: faker.name.lastName(),
|
||||
middle_name: '',
|
||||
birthdate: '2023-04-01',
|
||||
phone: '+1234567890',
|
||||
email: email2,
|
||||
full_address: 'Russia, Moscow, Tverskaya street, 2',
|
||||
full_address: 'Russia, Moscow, Tverskaya street, 1',
|
||||
},
|
||||
block_num: 0,
|
||||
deleted: false,
|
||||
};
|
||||
|
||||
const email3 = faker.internet.email().toLowerCase();
|
||||
export const admin = {
|
||||
_id: mongoose.Types.ObjectId(),
|
||||
name: faker.name.findName(),
|
||||
const usernameTwo = generateUsername();
|
||||
export const userTwo: testUser = {
|
||||
_id: generateRandomId(),
|
||||
email: email3,
|
||||
has_account: false,
|
||||
password,
|
||||
role: 'admin',
|
||||
status: 'active',
|
||||
message: '',
|
||||
is_registered: true,
|
||||
role: 'user',
|
||||
is_email_verified: false,
|
||||
signature: '',
|
||||
signature_hash: '',
|
||||
username: generateUsername(),
|
||||
username: usernameTwo,
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
statement: {
|
||||
hash: '',
|
||||
meta: {},
|
||||
public_key: '',
|
||||
signature: '',
|
||||
},
|
||||
referer: '',
|
||||
type: 'individual',
|
||||
individual_data: {
|
||||
username: usernameTwo,
|
||||
first_name: faker.name.firstName(),
|
||||
last_name: faker.name.lastName(),
|
||||
middle_name: '',
|
||||
birthdate: '2023-04-01',
|
||||
phone: '+1234567890',
|
||||
email: email3,
|
||||
full_address: 'Russia, Moscow, Tverskaya street, 3',
|
||||
full_address: 'Russia, Moscow, Tverskaya street, 2',
|
||||
},
|
||||
block_num: 0,
|
||||
deleted: false,
|
||||
};
|
||||
|
||||
export const insertUsers = async (users: any[]) => {
|
||||
await User.insertMany(users.map((user: any) => ({ ...user, password: hashedPassword })));
|
||||
export const chairman: testUser = {
|
||||
_id: generateRandomId(),
|
||||
email: email4,
|
||||
has_account: false,
|
||||
password,
|
||||
status: 'active',
|
||||
message: '',
|
||||
is_registered: true,
|
||||
role: 'user',
|
||||
is_email_verified: false,
|
||||
username: 'ant',
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
statement: {
|
||||
hash: '',
|
||||
meta: {},
|
||||
public_key: '',
|
||||
signature: '',
|
||||
},
|
||||
referer: '',
|
||||
type: 'individual',
|
||||
individual_data: {
|
||||
username: 'ant',
|
||||
first_name: faker.name.firstName(),
|
||||
last_name: faker.name.lastName(),
|
||||
middle_name: '',
|
||||
birthdate: '2023-04-01',
|
||||
phone: '+1234567890',
|
||||
email: email4,
|
||||
full_address: 'Russia, Moscow, Tverskaya street, 1',
|
||||
},
|
||||
block_num: 0,
|
||||
deleted: false,
|
||||
};
|
||||
|
||||
export const voskhod: testUser = {
|
||||
_id: generateRandomId(),
|
||||
email: email5,
|
||||
password,
|
||||
has_account: false,
|
||||
status: 'active',
|
||||
message: '',
|
||||
is_registered: true,
|
||||
role: 'user',
|
||||
is_email_verified: false,
|
||||
username: 'voskhod',
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
statement: {
|
||||
hash: '',
|
||||
meta: {},
|
||||
public_key: '',
|
||||
signature: '',
|
||||
},
|
||||
referer: '',
|
||||
type: 'organization',
|
||||
organization_data: {
|
||||
username: 'voskhod',
|
||||
type: 'coop',
|
||||
is_cooperative: true,
|
||||
short_name: '"ПК Восход"',
|
||||
full_name: 'Потребительский Кооператив "ВОСХОД"',
|
||||
represented_by: {
|
||||
first_name: 'Имя',
|
||||
last_name: 'Фамилия',
|
||||
middle_name: 'Отчество',
|
||||
position: 'Председатель',
|
||||
based_on: 'Решения общего собрания №1',
|
||||
},
|
||||
country: 'Russia',
|
||||
city: 'Москва',
|
||||
full_address: 'Переулок Правды, дом 1',
|
||||
email: email5,
|
||||
phone: '+71234567890',
|
||||
details: {
|
||||
inn: '1234567890',
|
||||
ogrn: '1234567890123',
|
||||
},
|
||||
bank_account: {
|
||||
account_number: '40817810099910004312',
|
||||
currency: 'RUB',
|
||||
card_number: '1234567890123456',
|
||||
bank_name: 'Sberbank',
|
||||
details: {
|
||||
bik: '123456789',
|
||||
corr: '30101810400000000225',
|
||||
kpp: '123456789',
|
||||
},
|
||||
},
|
||||
},
|
||||
block_num: 0,
|
||||
deleted: false,
|
||||
};
|
||||
|
||||
export const insertPrivateEntrepreneurUserData = async (data: any) => {
|
||||
const collection = mongoose.connection.db.collection('EntrepreneurData'); // Замените на имя вашей коллекции
|
||||
await collection.insertOne({ ...data });
|
||||
};
|
||||
|
||||
export const insertPrivateIndividualUserData = async (data: any) => {
|
||||
const collection = mongoose.connection.db.collection('IndividualData'); // Замените на имя вашей коллекции
|
||||
await collection.insertOne({ ...data });
|
||||
};
|
||||
|
||||
export const insertPrivateOrganizationUserData = async (data: any) => {
|
||||
const collection = mongoose.connection.db.collection('OrgData'); // Замените на имя вашей коллекции
|
||||
await collection.insertOne({ ...data });
|
||||
};
|
||||
|
||||
export interface IPaymentData {
|
||||
username: string;
|
||||
method_id: number;
|
||||
user_type: 'individual' | 'entrepreneur' | 'organization';
|
||||
method_type: 'sbp' | 'bank_transfer';
|
||||
is_default: boolean;
|
||||
data: any;
|
||||
}
|
||||
|
||||
export const insertPaymentMethod = async (data: IPaymentData, block_num: number) => {
|
||||
const collection = mongoose.connection.db.collection('PaymentData'); // Замените на имя вашей коллекции
|
||||
await collection.insertOne({ ...data, block_num, _created_at: new Date(), deleted: false });
|
||||
};
|
||||
|
||||
export const insertUsers = async (users: testUser[]) => {
|
||||
for (const user of users) {
|
||||
const { type, individual_data, organization_data, entrepreneur_data, block_num, ...rest } = user;
|
||||
|
||||
await User.insertMany([{ ...rest, type, password: hashedPassword }]);
|
||||
|
||||
if (type === 'individual' && individual_data) {
|
||||
await insertPrivateIndividualUserData({
|
||||
...individual_data,
|
||||
username: rest.username,
|
||||
block_num,
|
||||
_created_at: new Date(),
|
||||
});
|
||||
} else if (type === 'organization' && organization_data) {
|
||||
const { bank_account, ...org_data } = organization_data;
|
||||
await insertPrivateOrganizationUserData({ ...org_data, username: rest.username, block_num, _created_at: new Date() });
|
||||
|
||||
await insertPaymentMethod(
|
||||
{
|
||||
username: rest.username,
|
||||
method_id: 1,
|
||||
user_type: 'organization',
|
||||
method_type: 'bank_transfer',
|
||||
is_default: true,
|
||||
data: bank_account,
|
||||
},
|
||||
block_num
|
||||
);
|
||||
} else if (type === 'entrepreneur' && entrepreneur_data) {
|
||||
const { bank_account, ...entr_data } = entrepreneur_data;
|
||||
|
||||
await insertPrivateEntrepreneurUserData({ ...entr_data, username: rest.username, block_num, _created_at: new Date() });
|
||||
|
||||
await insertPaymentMethod(
|
||||
{
|
||||
username: rest.username,
|
||||
method_id: 1,
|
||||
user_type: 'organization',
|
||||
method_type: 'bank_transfer',
|
||||
is_default: true,
|
||||
data: bank_account,
|
||||
},
|
||||
block_num
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -15,30 +15,61 @@ import { roleRights } from '../../src/config/roles';
|
||||
import { tokenTypes } from '../../src/config/tokens';
|
||||
import { userOne, admin, insertUsers } from '../fixtures/user.fixture';
|
||||
import { userOneAccessToken, adminAccessToken } from '../fixtures/token.fixture';
|
||||
import { generateUsername } from '../utils/generateUsername';
|
||||
import { getBlockchainInfo } from '../../src/services/blockchain.service';
|
||||
import { Bytes, Checksum256, PrivateKey } from '@wharfkit/session';
|
||||
|
||||
setupTestDB();
|
||||
|
||||
describe('Auth routes', () => {
|
||||
describe('POST /v1/auth/register', () => {
|
||||
describe('POST /v1/users', () => {
|
||||
let newUser;
|
||||
beforeEach(() => {
|
||||
const email = faker.internet.email().toLowerCase();
|
||||
|
||||
newUser = {
|
||||
name: faker.name.findName(),
|
||||
email: faker.internet.email().toLowerCase(),
|
||||
email,
|
||||
password: 'password1',
|
||||
role: 'user',
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
username: generateUsername(),
|
||||
referer: '',
|
||||
type: 'individual',
|
||||
individual_data: {
|
||||
first_name: faker.name.firstName(),
|
||||
last_name: faker.name.lastName(),
|
||||
middle_name: '',
|
||||
birthdate: '2023-04-01',
|
||||
phone: '+1234567890',
|
||||
email: email,
|
||||
full_address: 'Russia, Moscow, Tverskaya street, 10',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
test('should return 201 and successfully register user if request data is ok', async () => {
|
||||
const res = await request(app).post('/v1/auth/register').send(newUser).expect(httpStatus.CREATED);
|
||||
const res = await request(app).post('/v1/users').send(newUser).expect(httpStatus.CREATED);
|
||||
|
||||
expect(res.body.user).not.toHaveProperty('password');
|
||||
|
||||
expect(res.body.user).toEqual({
|
||||
id: expect.anything(),
|
||||
name: newUser.name,
|
||||
email: newUser.email,
|
||||
role: 'user',
|
||||
is_email_verified: false,
|
||||
has_account: false,
|
||||
is_registered: false,
|
||||
public_key: newUser.public_key,
|
||||
referer: newUser.referer,
|
||||
status: 'created',
|
||||
type: 'individual',
|
||||
username: newUser.username,
|
||||
statement: {
|
||||
hash: '',
|
||||
public_key: '',
|
||||
signature: '',
|
||||
meta: {},
|
||||
},
|
||||
});
|
||||
|
||||
const dbUser = await User.findById(res.body.user.id);
|
||||
@@ -46,7 +77,7 @@ describe('Auth routes', () => {
|
||||
expect(dbUser).toBeDefined();
|
||||
|
||||
expect(dbUser?.password).not.toBe(newUser.password);
|
||||
expect(dbUser).toMatchObject({ name: newUser.name, email: newUser.email, role: 'user', is_email_verified: false });
|
||||
expect(dbUser).toMatchObject({ email: newUser.email, role: 'user', is_email_verified: false });
|
||||
|
||||
expect(res.body.tokens).toEqual({
|
||||
access: { token: expect.anything(), expires: expect.anything() },
|
||||
@@ -57,46 +88,58 @@ describe('Auth routes', () => {
|
||||
test('should return 400 error if email is invalid', async () => {
|
||||
newUser.email = 'invalidEmail';
|
||||
|
||||
await request(app).post('/v1/auth/register').send(newUser).expect(httpStatus.BAD_REQUEST);
|
||||
await request(app).post('/v1/users').send(newUser).expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 400 error if email is already used', async () => {
|
||||
await insertUsers([userOne]);
|
||||
newUser.email = userOne.email;
|
||||
|
||||
await request(app).post('/v1/auth/register').send(newUser).expect(httpStatus.BAD_REQUEST);
|
||||
await request(app).post('/v1/users').send(newUser).expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 400 error if password length is less than 8 characters', async () => {
|
||||
newUser.password = 'passwo1';
|
||||
|
||||
await request(app).post('/v1/auth/register').send(newUser).expect(httpStatus.BAD_REQUEST);
|
||||
await request(app).post('/v1/users').send(newUser).expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 400 error if password does not contain both letters and numbers', async () => {
|
||||
newUser.password = 'password';
|
||||
|
||||
await request(app).post('/v1/auth/register').send(newUser).expect(httpStatus.BAD_REQUEST);
|
||||
await request(app).post('/v1/users').send(newUser).expect(httpStatus.BAD_REQUEST);
|
||||
|
||||
newUser.password = '11111111';
|
||||
|
||||
await request(app).post('/v1/auth/register').send(newUser).expect(httpStatus.BAD_REQUEST);
|
||||
await request(app).post('/v1/users').send(newUser).expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /v1/auth/login', () => {
|
||||
test('should return 200 and login user if email and password match', async () => {
|
||||
await insertUsers([userOne]);
|
||||
|
||||
const now = (await getBlockchainInfo()).head_block_time;
|
||||
|
||||
const privateKey = PrivateKey.fromString('5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3');
|
||||
|
||||
const bytes = Bytes.fromString(now, 'utf8');
|
||||
const checksum = Checksum256.hash(bytes);
|
||||
const signature = privateKey.signDigest(checksum);
|
||||
|
||||
const loginCredentials = {
|
||||
email: userOne.email,
|
||||
password: userOne.password,
|
||||
now,
|
||||
signature,
|
||||
};
|
||||
|
||||
const res = await request(app).post('/v1/auth/login').send(loginCredentials).expect(httpStatus.OK);
|
||||
const res = await request(app).post('/v1/auth/login').send(loginCredentials);
|
||||
|
||||
expect(res.body.user).toEqual({
|
||||
expect(res.status).toEqual(httpStatus.OK);
|
||||
|
||||
expect(res.body.user).toMatchObject({
|
||||
id: expect.anything(),
|
||||
name: userOne.name,
|
||||
username: userOne.username,
|
||||
email: userOne.email,
|
||||
role: userOne.role,
|
||||
is_email_verified: userOne.is_email_verified,
|
||||
@@ -109,27 +152,38 @@ describe('Auth routes', () => {
|
||||
});
|
||||
|
||||
test('should return 401 error if there are no users with that email', async () => {
|
||||
const now = (await getBlockchainInfo()).head_block_time;
|
||||
|
||||
const loginCredentials = {
|
||||
now,
|
||||
email: userOne.email,
|
||||
password: userOne.password,
|
||||
signature: userOne.password,
|
||||
};
|
||||
|
||||
const res = await request(app).post('/v1/auth/login').send(loginCredentials).expect(httpStatus.UNAUTHORIZED);
|
||||
|
||||
expect(res.body).toEqual({ code: httpStatus.UNAUTHORIZED, message: 'Incorrect email or password' });
|
||||
expect(res.body).toEqual({ code: httpStatus.UNAUTHORIZED, message: 'Пользователь не найден' });
|
||||
});
|
||||
|
||||
test('should return 401 error if password is wrong', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const loginCredentials = {
|
||||
email: userOne.email,
|
||||
password: 'wrongPassword1',
|
||||
};
|
||||
|
||||
const res = await request(app).post('/v1/auth/login').send(loginCredentials).expect(httpStatus.UNAUTHORIZED);
|
||||
|
||||
expect(res.body).toEqual({ code: httpStatus.UNAUTHORIZED, message: 'Incorrect email or password' });
|
||||
});
|
||||
// test('should return 401 error if signature is wrong', async () => {
|
||||
// Восстановить проверку. Необходимо подложить аккаунт тестового юзера в коде вместо обращения к блокчейну.
|
||||
// await insertUsers([userOne]);
|
||||
// const now = (await getBlockchainInfo()).head_block_time;
|
||||
// const privateKey = PrivateKey.fromString('5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3');
|
||||
// const bytes = Bytes.fromString(now, 'utf8');
|
||||
// const checksum = Checksum256.hash(bytes);
|
||||
// const signature = privateKey.signDigest(checksum);
|
||||
// const now2 = '2024-08-02T11:50:40.000';
|
||||
// const loginCredentials = {
|
||||
// email: userOne.email,
|
||||
// now: now2,
|
||||
// signature,
|
||||
// };
|
||||
// const res = await request(app).post('/v1/auth/login').send(loginCredentials);
|
||||
// console.log(res.body);
|
||||
// expect(res.status).toBe(httpStatus.UNAUTHORIZED);
|
||||
// expect(res.body).toEqual({ code: httpStatus.UNAUTHORIZED, message: 'Incorrect email or password' });
|
||||
// });
|
||||
});
|
||||
|
||||
describe('POST /v1/auth/logout', () => {
|
||||
@@ -146,7 +200,8 @@ describe('Auth routes', () => {
|
||||
});
|
||||
|
||||
test('should return 400 error if refresh token is missing from request body', async () => {
|
||||
await request(app).post('/v1/auth/logout').send().expect(httpStatus.BAD_REQUEST);
|
||||
const res = await request(app).post('/v1/auth/logout').send();
|
||||
expect(res.status).toBe(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 404 error if refresh token is not found in the database', async () => {
|
||||
@@ -173,70 +228,62 @@ describe('Auth routes', () => {
|
||||
const expires = moment().add(config.jwt.refreshExpirationDays, 'days');
|
||||
const refreshToken = tokenService.generateToken(userOne._id, expires, tokenTypes.REFRESH);
|
||||
await tokenService.saveToken(refreshToken, userOne._id, expires, tokenTypes.REFRESH);
|
||||
|
||||
const res = await request(app).post('/v1/auth/refresh-tokens').send({ refreshToken }).expect(httpStatus.OK);
|
||||
|
||||
const res = await request(app).post('/v1/auth/refresh-tokens').send({ refreshToken });
|
||||
expect(res.status).toBe(httpStatus.OK);
|
||||
expect(res.body).toEqual({
|
||||
access: { token: expect.anything(), expires: expect.anything() },
|
||||
refresh: { token: expect.anything(), expires: expect.anything() },
|
||||
});
|
||||
|
||||
const dbRefreshTokenDoc = await Token.findOne({ token: res.body.refresh.token });
|
||||
expect(dbRefreshTokenDoc).toMatchObject({ type: tokenTypes.REFRESH, user: userOne._id, blacklisted: false });
|
||||
|
||||
expect(dbRefreshTokenDoc).toHaveProperty('blacklisted');
|
||||
expect(dbRefreshTokenDoc).toHaveProperty('token');
|
||||
expect(dbRefreshTokenDoc).toHaveProperty('user');
|
||||
expect(dbRefreshTokenDoc).toHaveProperty('expires');
|
||||
expect(dbRefreshTokenDoc).toHaveProperty('type');
|
||||
expect(dbRefreshTokenDoc).toHaveProperty('createdAt');
|
||||
expect(dbRefreshTokenDoc).toHaveProperty('updatedAt');
|
||||
const dbRefreshTokenCount = await Token.countDocuments();
|
||||
expect(dbRefreshTokenCount).toBe(1);
|
||||
});
|
||||
|
||||
test('should return 400 error if refresh token is missing from request body', async () => {
|
||||
await request(app).post('/v1/auth/refresh-tokens').send().expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 401 error if refresh token is signed using an invalid secret', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.refreshExpirationDays, 'days');
|
||||
const refreshToken = tokenService.generateToken(userOne._id, expires, tokenTypes.REFRESH, 'invalidSecret');
|
||||
await tokenService.saveToken(refreshToken, userOne._id, expires, tokenTypes.REFRESH);
|
||||
|
||||
await request(app).post('/v1/auth/refresh-tokens').send({ refreshToken }).expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('should return 401 error if refresh token is not found in the database', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.refreshExpirationDays, 'days');
|
||||
const refreshToken = tokenService.generateToken(userOne._id, expires, tokenTypes.REFRESH);
|
||||
|
||||
await request(app).post('/v1/auth/refresh-tokens').send({ refreshToken }).expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('should return 401 error if refresh token is blacklisted', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.refreshExpirationDays, 'days');
|
||||
const refreshToken = tokenService.generateToken(userOne._id, expires, tokenTypes.REFRESH);
|
||||
await tokenService.saveToken(refreshToken, userOne._id, expires, tokenTypes.REFRESH, true);
|
||||
|
||||
await request(app).post('/v1/auth/refresh-tokens').send({ refreshToken }).expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('should return 401 error if refresh token is expired', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().subtract(1, 'minutes');
|
||||
const refreshToken = tokenService.generateToken(userOne._id, expires);
|
||||
const refreshToken = tokenService.generateToken(userOne._id, expires, tokenTypes.REFRESH);
|
||||
await tokenService.saveToken(refreshToken, userOne._id, expires, tokenTypes.REFRESH);
|
||||
|
||||
await request(app).post('/v1/auth/refresh-tokens').send({ refreshToken }).expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('should return 401 error if user is not found', async () => {
|
||||
const expires = moment().add(config.jwt.refreshExpirationDays, 'days');
|
||||
const refreshToken = tokenService.generateToken(userOne._id, expires, tokenTypes.REFRESH);
|
||||
await tokenService.saveToken(refreshToken, userOne._id, expires, tokenTypes.REFRESH);
|
||||
|
||||
await request(app).post('/v1/auth/refresh-tokens').send({ refreshToken }).expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /v1/auth/forgot-password', () => {
|
||||
describe('POST /v1/auth/lost-key', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(emailService.transport, 'sendMail').mockResolvedValue();
|
||||
});
|
||||
@@ -245,114 +292,115 @@ describe('Auth routes', () => {
|
||||
await insertUsers([userOne]);
|
||||
const sendResetPasswordEmailSpy = jest.spyOn(emailService, 'sendResetPasswordEmail');
|
||||
|
||||
await request(app).post('/v1/auth/forgot-password').send({ email: userOne.email }).expect(httpStatus.NO_CONTENT);
|
||||
await request(app).post('/v1/auth/lost-key').send({ email: userOne.email }).expect(httpStatus.NO_CONTENT);
|
||||
|
||||
expect(sendResetPasswordEmailSpy).toHaveBeenCalledWith(userOne.email, expect.any(String));
|
||||
const resetPasswordToken = sendResetPasswordEmailSpy.mock.calls[0][1];
|
||||
const dbResetPasswordTokenDoc = await Token.findOne({ token: resetPasswordToken, user: userOne._id });
|
||||
const resetKeyToken = sendResetPasswordEmailSpy.mock.calls[0][1];
|
||||
const dbResetPasswordTokenDoc = await Token.findOne({ token: resetKeyToken, user: userOne._id.toString() });
|
||||
expect(dbResetPasswordTokenDoc).toBeDefined();
|
||||
});
|
||||
|
||||
test('should return 400 if email is missing', async () => {
|
||||
await insertUsers([userOne]);
|
||||
|
||||
await request(app).post('/v1/auth/forgot-password').send().expect(httpStatus.BAD_REQUEST);
|
||||
const res = await request(app).post('/v1/auth/lost-key').send();
|
||||
|
||||
expect(res.status).toBe(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 404 if email does not belong to any user', async () => {
|
||||
await request(app).post('/v1/auth/forgot-password').send({ email: userOne.email }).expect(httpStatus.NOT_FOUND);
|
||||
await request(app).post('/v1/auth/lost-key').send({ email: userOne.email }).expect(httpStatus.NOT_FOUND);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /v1/auth/reset-password', () => {
|
||||
describe('POST /v1/auth/reset-key', () => {
|
||||
test('should return 204 and reset the password', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.resetPasswordExpirationMinutes, 'minutes');
|
||||
const resetPasswordToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetPasswordToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
const resetKeyToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetKeyToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'password2' })
|
||||
.expect(httpStatus.NO_CONTENT);
|
||||
const res = await request(app).post('/v1/auth/reset-key').send({ token: resetKeyToken, password: 'password2' });
|
||||
|
||||
expect(res.status).toBe(httpStatus.NO_CONTENT);
|
||||
|
||||
const dbUser = await User.findById(userOne._id);
|
||||
const isPasswordMatch = await bcrypt.compare('password2', dbUser.password);
|
||||
expect(isPasswordMatch).toBe(true);
|
||||
expect(dbUser).not.toBeUndefined();
|
||||
|
||||
const dbResetPasswordTokenCount = await Token.countDocuments({ user: userOne._id, type: tokenTypes.RESET_PASSWORD });
|
||||
expect(dbResetPasswordTokenCount).toBe(0);
|
||||
if (dbUser) {
|
||||
const isPasswordMatch = await bcrypt.compare('password2', dbUser.password);
|
||||
expect(isPasswordMatch).toBe(true);
|
||||
|
||||
const dbResetPasswordTokenCount = await Token.countDocuments({
|
||||
user: userOne._id.toString(),
|
||||
type: tokenTypes.RESET_PASSWORD,
|
||||
});
|
||||
expect(dbResetPasswordTokenCount).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
test('should return 400 if reset password token is missing', async () => {
|
||||
await insertUsers([userOne]);
|
||||
|
||||
await request(app).post('/v1/auth/reset-password').send({ password: 'password2' }).expect(httpStatus.BAD_REQUEST);
|
||||
await request(app).post('/v1/auth/reset-key').send({ password: 'password2' }).expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 401 if reset password token is blacklisted', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.resetPasswordExpirationMinutes, 'minutes');
|
||||
const resetPasswordToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetPasswordToken, userOne._id, expires, tokenTypes.RESET_PASSWORD, true);
|
||||
const resetKeyToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetKeyToken, userOne._id, expires, tokenTypes.RESET_PASSWORD, true);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'password2' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV' })
|
||||
.expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('should return 401 if reset password token is expired', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().subtract(1, 'minutes');
|
||||
const resetPasswordToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetPasswordToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
const resetKeyToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetKeyToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'password2' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV' })
|
||||
.expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('should return 401 if user is not found', async () => {
|
||||
const expires = moment().add(config.jwt.resetPasswordExpirationMinutes, 'minutes');
|
||||
const resetPasswordToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetPasswordToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
const resetKeyToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetKeyToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'password2' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV' })
|
||||
.expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('should return 400 if password is missing or invalid', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.resetPasswordExpirationMinutes, 'minutes');
|
||||
const resetPasswordToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetPasswordToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
const resetKeyToken = tokenService.generateToken(userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
await tokenService.saveToken(resetKeyToken, userOne._id, expires, tokenTypes.RESET_PASSWORD);
|
||||
|
||||
await request(app).post('/v1/auth/reset-password').query({ token: resetPasswordToken }).expect(httpStatus.BAD_REQUEST);
|
||||
await request(app).post('/v1/auth/reset-key').send({ token: resetKeyToken }).expect(httpStatus.BAD_REQUEST);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'short1' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: 'stort' })
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: 'password' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: 'password' })
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
|
||||
await request(app)
|
||||
.post('/v1/auth/reset-password')
|
||||
.query({ token: resetPasswordToken })
|
||||
.send({ password: '11111111' })
|
||||
.post('/v1/auth/reset-key')
|
||||
.send({ token: resetKeyToken, public_key: '11111111' })
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
});
|
||||
@@ -373,7 +421,7 @@ describe('Auth routes', () => {
|
||||
|
||||
expect(sendVerificationEmailSpy).toHaveBeenCalledWith(userOne.email, expect.any(String));
|
||||
const verifyEmailToken = sendVerificationEmailSpy.mock.calls[0][1];
|
||||
const dbVerifyEmailToken = await Token.findOne({ token: verifyEmailToken, user: userOne._id });
|
||||
const dbVerifyEmailToken = await Token.findOne({ token: verifyEmailToken, user: userOne._id.toString() });
|
||||
|
||||
expect(dbVerifyEmailToken).toBeDefined();
|
||||
});
|
||||
@@ -389,7 +437,7 @@ describe('Auth routes', () => {
|
||||
test('should return 204 and verify the email', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.verifyEmailExpirationMinutes, 'minutes');
|
||||
const verifyEmailToken = tokenService.generateToken(userOne._id, expires);
|
||||
const verifyEmailToken = tokenService.generateToken(userOne._id, expires, tokenTypes.VERIFY_EMAIL);
|
||||
await tokenService.saveToken(verifyEmailToken, userOne._id, expires, tokenTypes.VERIFY_EMAIL);
|
||||
|
||||
await request(app)
|
||||
@@ -400,10 +448,14 @@ describe('Auth routes', () => {
|
||||
|
||||
const dbUser = await User.findById(userOne._id);
|
||||
|
||||
expect(dbUser).not.toBeUndefined();
|
||||
|
||||
if (!dbUser) return;
|
||||
|
||||
expect(dbUser.is_email_verified).toBe(true);
|
||||
|
||||
const dbVerifyEmailToken = await Token.countDocuments({
|
||||
user: userOne._id,
|
||||
user: userOne._id.toString(),
|
||||
type: tokenTypes.VERIFY_EMAIL,
|
||||
});
|
||||
expect(dbVerifyEmailToken).toBe(0);
|
||||
@@ -418,7 +470,7 @@ describe('Auth routes', () => {
|
||||
test('should return 401 if verify email token is blacklisted', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.verifyEmailExpirationMinutes, 'minutes');
|
||||
const verifyEmailToken = tokenService.generateToken(userOne._id, expires);
|
||||
const verifyEmailToken = tokenService.generateToken(userOne._id, expires, tokenTypes.VERIFY_EMAIL);
|
||||
await tokenService.saveToken(verifyEmailToken, userOne._id, expires, tokenTypes.VERIFY_EMAIL, true);
|
||||
|
||||
await request(app)
|
||||
@@ -431,7 +483,7 @@ describe('Auth routes', () => {
|
||||
test('should return 401 if verify email token is expired', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().subtract(1, 'minutes');
|
||||
const verifyEmailToken = tokenService.generateToken(userOne._id, expires);
|
||||
const verifyEmailToken = tokenService.generateToken(userOne._id, expires, tokenTypes.VERIFY_EMAIL);
|
||||
await tokenService.saveToken(verifyEmailToken, userOne._id, expires, tokenTypes.VERIFY_EMAIL);
|
||||
|
||||
await request(app)
|
||||
@@ -443,7 +495,7 @@ describe('Auth routes', () => {
|
||||
|
||||
test('should return 401 if user is not found', async () => {
|
||||
const expires = moment().add(config.jwt.verifyEmailExpirationMinutes, 'minutes');
|
||||
const verifyEmailToken = tokenService.generateToken(userOne._id, expires);
|
||||
const verifyEmailToken = tokenService.generateToken(userOne._id, expires, tokenTypes.VERIFY_EMAIL);
|
||||
await tokenService.saveToken(verifyEmailToken, userOne._id, expires, tokenTypes.VERIFY_EMAIL);
|
||||
|
||||
await request(app)
|
||||
@@ -453,137 +505,140 @@ describe('Auth routes', () => {
|
||||
.expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Auth middleware', () => {
|
||||
test('should call next with no errors if access token is valid', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${userOneAccessToken}` } });
|
||||
const next = jest.fn();
|
||||
describe('Auth middleware', () => {
|
||||
test('should call next with no errors if access token is valid', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${userOneAccessToken}` } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith();
|
||||
expect(req.user._id).toEqual(userOne._id);
|
||||
});
|
||||
|
||||
test('should call next with unauthorized error if access token is not found in header', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const req = httpMocks.createRequest();
|
||||
const next = jest.fn();
|
||||
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with unauthorized error if access token is not a valid jwt token', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: 'Bearer randomToken' } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with unauthorized error if the token is not an access token', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.accessExpirationMinutes, 'minutes');
|
||||
const refreshToken = tokenService.generateToken(userOne._id, expires, tokenTypes.REFRESH);
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${refreshToken}` } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with unauthorized error if access token is generated with an invalid secret', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.accessExpirationMinutes, 'minutes');
|
||||
const accessToken = tokenService.generateToken(userOne._id, expires, tokenTypes.ACCESS, 'invalidSecret');
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${accessToken}` } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with unauthorized error if access token is expired', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().subtract(1, 'minutes');
|
||||
const accessToken = tokenService.generateToken(userOne._id, expires, tokenTypes.ACCESS);
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${accessToken}` } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with unauthorized error if user is not found', async () => {
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${userOneAccessToken}` } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with forbidden error if user does not have required rights and userId is not in params', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${userOneAccessToken}` } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth('anyRight')(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(expect.objectContaining({ statusCode: httpStatus.FORBIDDEN, message: 'Forbidden' }));
|
||||
});
|
||||
|
||||
test('should call next with no errors if user does not have required rights but userId is in params', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const req = httpMocks.createRequest({
|
||||
headers: { Authorization: `Bearer ${userOneAccessToken}` },
|
||||
params: { userId: userOne._id.toHexString() },
|
||||
expect(next).toHaveBeenCalledWith();
|
||||
expect(req.user._id).toEqual(userOne._id);
|
||||
});
|
||||
const next = jest.fn();
|
||||
|
||||
await auth('anyRight')(req, httpMocks.createResponse(), next);
|
||||
test('should call next with unauthorized error if access token is not found in header', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const req = httpMocks.createRequest();
|
||||
const next = jest.fn();
|
||||
|
||||
expect(next).toHaveBeenCalledWith();
|
||||
});
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
test('should call next with no errors if user has required rights', async () => {
|
||||
await insertUsers([admin]);
|
||||
const req = httpMocks.createRequest({
|
||||
headers: { Authorization: `Bearer ${adminAccessToken}` },
|
||||
params: { userId: userOne._id.toHexString() },
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
const next = jest.fn();
|
||||
|
||||
await auth(...roleRights.get('admin'))(req, httpMocks.createResponse(), next);
|
||||
test('should call next with unauthorized error if access token is not a valid jwt token', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: 'Bearer randomToken' } });
|
||||
const next = jest.fn();
|
||||
|
||||
expect(next).toHaveBeenCalledWith();
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with unauthorized error if the token is not an access token', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.accessExpirationMinutes, 'minutes');
|
||||
const refreshToken = tokenService.generateToken(userOne._id, expires, tokenTypes.REFRESH);
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${refreshToken}` } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with unauthorized error if access token is generated with an invalid secret', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().add(config.jwt.accessExpirationMinutes, 'minutes');
|
||||
const accessToken = tokenService.generateToken(userOne._id, expires, tokenTypes.ACCESS, 'invalidSecret');
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${accessToken}` } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with unauthorized error if access token is expired', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const expires = moment().subtract(1, 'minutes');
|
||||
const accessToken = tokenService.generateToken(userOne._id, expires, tokenTypes.ACCESS);
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${accessToken}` } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with unauthorized error if user is not found', async () => {
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${userOneAccessToken}` } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth()(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.UNAUTHORIZED, message: 'Please authenticate' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with forbidden error if user does not have required rights and userId is not in params', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const req = httpMocks.createRequest({ headers: { Authorization: `Bearer ${userOneAccessToken}` } });
|
||||
const next = jest.fn();
|
||||
|
||||
await auth('anyRight')(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith(expect.any(ApiError));
|
||||
expect(next).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ statusCode: httpStatus.FORBIDDEN, message: 'Недостаточно прав доступа' })
|
||||
);
|
||||
});
|
||||
|
||||
test('should call next with no errors if user does not have required rights but username is in params', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const req = httpMocks.createRequest({
|
||||
headers: { Authorization: `Bearer ${userOneAccessToken}` },
|
||||
params: { username: userOne.username },
|
||||
});
|
||||
const next = jest.fn();
|
||||
|
||||
await auth('anyRight')(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
test('should call next with no errors if user has required rights', async () => {
|
||||
await insertUsers([admin]);
|
||||
const req = httpMocks.createRequest({
|
||||
headers: { Authorization: `Bearer ${adminAccessToken}` },
|
||||
params: { userId: userOne._id.toHexString() },
|
||||
});
|
||||
const next = jest.fn();
|
||||
|
||||
const chairmanRights = roleRights.get('chairman') || [];
|
||||
await auth(...chairmanRights)(req, httpMocks.createResponse(), next);
|
||||
|
||||
expect(next).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,42 +7,59 @@ import { generateUsername } from '../utils/generateUsername';
|
||||
import { User } from '../../src/models';
|
||||
import { IGenerateJoinCoop, IGeneratedDocument, IIndividualData } from 'coopdoc-generator-ts';
|
||||
import { IDocument, IJoinCooperative } from '../../src/types';
|
||||
import ecc from 'eosjs-ecc'
|
||||
import { admin, insertUsers, userOne, userTwo } from '../fixtures/user.fixture';
|
||||
import { adminAccessToken } from '../fixtures/token.fixture';
|
||||
import ecc from 'eosjs-ecc';
|
||||
import { admin, chairman, insertUsers, userOne, userTwo, voskhod } from '../fixtures/user.fixture';
|
||||
import { adminAccessToken, userOneAccessToken } from '../fixtures/token.fixture';
|
||||
import { fixtureAction, insertAction, insertActions, installInitialCooperativeData } from '../fixtures/document.fixture';
|
||||
import { SovietContract } from 'cooptypes';
|
||||
import { participantOne } from '../fixtures/participant.fixture';
|
||||
|
||||
const public_key = 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV';
|
||||
|
||||
setupTestDB();
|
||||
|
||||
describe('Проверка получения документов', () => {
|
||||
describe('GET /v1/data/get-documents', () => {
|
||||
|
||||
test('Нельзя без авторизации админа получить все документы', async () => {
|
||||
await insertUsers([admin]);
|
||||
|
||||
const documents = await request(app).get('/v1/data/get-documents').send()
|
||||
describe('GET /v1/documents/get-documents', () => {
|
||||
test('Нельзя без авторизации получить все документы', async () => {
|
||||
const documents = await request(app).get('/v1/documents/get-documents').send();
|
||||
|
||||
expect(documents.status).toBe(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('Успешное получение списка документов', async () => {
|
||||
await insertUsers([admin]);
|
||||
installInitialCooperativeData()
|
||||
test('Нельзя без авторизации админа получить все документы', async () => {
|
||||
await insertUsers([userOne]);
|
||||
|
||||
const registeredUser = await request(app).post('/v1/users').send(userOne)
|
||||
const documents = await request(app)
|
||||
.get('/v1/documents/get-documents')
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send();
|
||||
|
||||
expect(documents.status).toBe(httpStatus.FORBIDDEN);
|
||||
});
|
||||
|
||||
test('Успешное генерация и извлечение списка документов', async () => {
|
||||
const coopData = voskhod;
|
||||
|
||||
await insertUsers([coopData, chairman, admin]);
|
||||
await installInitialCooperativeData();
|
||||
|
||||
const registeredUser = await request(app).post('/v1/users').send(participantOne);
|
||||
|
||||
expect(registeredUser.status).toBe(httpStatus.CREATED);
|
||||
|
||||
const options: IGenerateJoinCoop = {
|
||||
action: 'joincoop',
|
||||
code: 'registrator',
|
||||
coopname: 'voskhod',
|
||||
username: userOne.username,
|
||||
signature: 'this is imaged signature'
|
||||
coopname: coopData.username,
|
||||
username: participantOne.username,
|
||||
signature: 'this is imaged signature',
|
||||
skip_save: false,
|
||||
};
|
||||
|
||||
let res = await request(app).post('/v1/data/generate').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(options);
|
||||
let res = await request(app)
|
||||
.post('/v1/documents/generate')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(options);
|
||||
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
expect(res.body.meta).toBeDefined();
|
||||
@@ -50,109 +67,157 @@ describe('Проверка получения документов', () => {
|
||||
expect(res.body.html).toBeDefined();
|
||||
expect(res.body.hash).toBeDefined();
|
||||
|
||||
const signature = await ecc.sign(res.body.hash, '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3')
|
||||
const signature = await ecc.sign(res.body.hash, '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3');
|
||||
|
||||
//имитируем событие парсинга заявления из блокчейна
|
||||
const newsubmitted_document = fixtureAction(1, SovietContract.Actions.Registry.NewSubmitted.actionName, userOne.username, 'voskhod', 'soviet', {
|
||||
coopname: 'voskhod',
|
||||
username: userOne.username,
|
||||
action: SovietContract.Actions.Registry.NewSubmitted.actionName,
|
||||
decision_id: 1,
|
||||
document: {
|
||||
meta: res.body.meta,
|
||||
hash: res.body.hash,
|
||||
signature,
|
||||
public_key: userOne.public_key
|
||||
}
|
||||
} as SovietContract.Actions.Registry.NewSubmitted.INewSubmitted)
|
||||
|
||||
await insertActions(newsubmitted_document)
|
||||
// //имитируем событие парсинга заявления из блокчейна
|
||||
const newsubmitted_document = fixtureAction(
|
||||
1,
|
||||
SovietContract.Actions.Registry.NewSubmitted.actionName,
|
||||
participantOne.username,
|
||||
coopData.username,
|
||||
'soviet',
|
||||
{
|
||||
coopname: coopData.username,
|
||||
username: participantOne.username,
|
||||
action: SovietContract.Actions.Registry.NewSubmitted.actionName,
|
||||
decision_id: 1,
|
||||
document: {
|
||||
meta: res.body.meta,
|
||||
hash: res.body.hash,
|
||||
signature,
|
||||
public_key: public_key,
|
||||
},
|
||||
} as SovietContract.Actions.Registry.NewSubmitted.INewSubmitted
|
||||
);
|
||||
|
||||
await insertActions(newsubmitted_document);
|
||||
|
||||
//добавляем голоса за решение
|
||||
//имитируем событие парсинга решения из блокчейна
|
||||
const vorfor_action1 = fixtureAction(1, SovietContract.Actions.Decisions.VoteFor.actionName, userOne.username, 'voskhod', 'soviet', {
|
||||
coopname: 'voskhod',
|
||||
member: 'ant',
|
||||
decision_id: "1",
|
||||
} as SovietContract.Actions.Decisions.VoteFor.IVoteForDecision)
|
||||
const vorfor_action1 = fixtureAction(
|
||||
1,
|
||||
SovietContract.Actions.Decisions.VoteFor.actionName,
|
||||
participantOne.username,
|
||||
coopData.username,
|
||||
'soviet',
|
||||
{
|
||||
coopname: coopData.username,
|
||||
member: 'ant',
|
||||
decision_id: '1',
|
||||
} as SovietContract.Actions.Decisions.VoteFor.IVoteForDecision
|
||||
);
|
||||
|
||||
await insertActions(vorfor_action1)
|
||||
await insertActions(vorfor_action1);
|
||||
|
||||
const vorfor_action2 = fixtureAction(1, SovietContract.Actions.Decisions.VoteFor.actionName, userOne.username, 'voskhod', 'soviet', {
|
||||
coopname: 'voskhod',
|
||||
member: 'ant',
|
||||
decision_id: "2",
|
||||
} as SovietContract.Actions.Decisions.VoteFor.IVoteForDecision)
|
||||
const vorfor_action2 = fixtureAction(
|
||||
1,
|
||||
SovietContract.Actions.Decisions.VoteFor.actionName,
|
||||
participantOne.username,
|
||||
coopData.username,
|
||||
'soviet',
|
||||
{
|
||||
coopname: coopData.username,
|
||||
member: 'ant',
|
||||
decision_id: '2',
|
||||
} as SovietContract.Actions.Decisions.VoteFor.IVoteForDecision
|
||||
);
|
||||
|
||||
await insertActions(vorfor_action2)
|
||||
await insertActions(vorfor_action2);
|
||||
|
||||
const vorfor_action3 = fixtureAction(1, SovietContract.Actions.Decisions.VoteFor.actionName,userOne.username, 'voskhod', 'soviet', {
|
||||
coopname: 'voskhod',
|
||||
member: 'ant',
|
||||
decision_id: "3",
|
||||
} as SovietContract.Actions.Decisions.VoteFor.IVoteForDecision)
|
||||
const vorfor_action3 = fixtureAction(
|
||||
1,
|
||||
SovietContract.Actions.Decisions.VoteFor.actionName,
|
||||
participantOne.username,
|
||||
coopData.username,
|
||||
'soviet',
|
||||
{
|
||||
coopname: coopData.username,
|
||||
member: 'ant',
|
||||
decision_id: '3',
|
||||
} as SovietContract.Actions.Decisions.VoteFor.IVoteForDecision
|
||||
);
|
||||
|
||||
await insertActions(vorfor_action3)
|
||||
await insertActions(vorfor_action3);
|
||||
|
||||
//TODO генерируем решение совета и добавляем его в блокчейн
|
||||
//generate
|
||||
|
||||
|
||||
//имитируем событие парсинга решения из блокчейна
|
||||
const newdecision_document = fixtureAction(1, SovietContract.Actions.Registry.NewDecision.actionName, userOne.username, 'voskhod', 'soviet', {
|
||||
coopname: 'voskhod',
|
||||
username: userOne.username,
|
||||
action: SovietContract.Actions.Registry.NewDecision.actionName,
|
||||
decision_id: "1",
|
||||
document: {
|
||||
meta: res.body.meta,
|
||||
hash: res.body.hash,
|
||||
signature,
|
||||
public_key: userOne.public_key
|
||||
}
|
||||
} as SovietContract.Actions.Registry.NewDecision.INewDecision)
|
||||
const newdecision_document = fixtureAction(
|
||||
1,
|
||||
SovietContract.Actions.Registry.NewDecision.actionName,
|
||||
participantOne.username,
|
||||
coopData.username,
|
||||
'soviet',
|
||||
{
|
||||
coopname: coopData.username,
|
||||
username: participantOne.username,
|
||||
action: SovietContract.Actions.Registry.NewDecision.actionName,
|
||||
decision_id: '1',
|
||||
document: {
|
||||
meta: res.body.meta,
|
||||
hash: res.body.hash,
|
||||
signature,
|
||||
public_key: public_key,
|
||||
},
|
||||
} as SovietContract.Actions.Registry.NewDecision.INewDecision
|
||||
);
|
||||
|
||||
await insertActions(newdecision_document)
|
||||
await insertActions(newdecision_document);
|
||||
|
||||
const newdecision_document2 = fixtureAction(1, SovietContract.Actions.Registry.NewDecision.actionName, userOne.username, 'voskhod', 'soviet', {
|
||||
coopname: 'voskhod',
|
||||
username: userOne.username,
|
||||
action: SovietContract.Actions.Registry.NewDecision.actionName,
|
||||
decision_id: "2",
|
||||
document: {
|
||||
meta: res.body.meta,
|
||||
hash: res.body.hash,
|
||||
signature,
|
||||
public_key: userOne.public_key
|
||||
}
|
||||
} as SovietContract.Actions.Registry.NewDecision.INewDecision)
|
||||
const newdecision_document2 = fixtureAction(
|
||||
1,
|
||||
SovietContract.Actions.Registry.NewDecision.actionName,
|
||||
participantOne.username,
|
||||
coopData.username,
|
||||
'soviet',
|
||||
{
|
||||
coopname: coopData.username,
|
||||
username: participantOne.username,
|
||||
action: SovietContract.Actions.Registry.NewDecision.actionName,
|
||||
decision_id: '2',
|
||||
document: {
|
||||
meta: res.body.meta,
|
||||
hash: res.body.hash,
|
||||
signature,
|
||||
public_key: public_key,
|
||||
},
|
||||
} as SovietContract.Actions.Registry.NewDecision.INewDecision
|
||||
);
|
||||
|
||||
await insertActions(newdecision_document2)
|
||||
await insertActions(newdecision_document2);
|
||||
|
||||
const newdecision_document3 = fixtureAction(1, SovietContract.Actions.Registry.NewDecision.actionName, userOne.username, 'voskhod', 'soviet', {
|
||||
coopname: 'voskhod',
|
||||
username: userOne.username,
|
||||
action: SovietContract.Actions.Registry.NewDecision.actionName,
|
||||
decision_id: "3",
|
||||
document: {
|
||||
meta: res.body.meta,
|
||||
hash: res.body.hash,
|
||||
signature,
|
||||
public_key: userOne.public_key
|
||||
}
|
||||
} as SovietContract.Actions.Registry.NewDecision.INewDecision)
|
||||
|
||||
await insertActions(newdecision_document3)
|
||||
const newdecision_document3 = fixtureAction(
|
||||
1,
|
||||
SovietContract.Actions.Registry.NewDecision.actionName,
|
||||
participantOne.username,
|
||||
coopData.username,
|
||||
'soviet',
|
||||
{
|
||||
coopname: coopData.username,
|
||||
username: participantOne.username,
|
||||
action: SovietContract.Actions.Registry.NewDecision.actionName,
|
||||
decision_id: '3',
|
||||
document: {
|
||||
meta: res.body.meta,
|
||||
hash: res.body.hash,
|
||||
signature,
|
||||
public_key: public_key,
|
||||
},
|
||||
} as SovietContract.Actions.Registry.NewDecision.INewDecision
|
||||
);
|
||||
|
||||
await insertActions(newdecision_document3);
|
||||
|
||||
//получаем документ
|
||||
const documents = await request(app).get('/v1/data/get-documents').set('Authorization', `Bearer ${adminAccessToken}`).query({ limit: 1 }).send()
|
||||
const documents = await request(app)
|
||||
.get('/v1/documents/get-documents')
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.query({ limit: 1 })
|
||||
.send();
|
||||
|
||||
console.log("documentss: ", documents.body)
|
||||
expect(documents.body.results.length).toBeDefined()
|
||||
expect(documents.body.results.length).toBeDefined();
|
||||
expect(documents.status).toBe(httpStatus.OK);
|
||||
|
||||
});
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,15 +7,23 @@ import { generateUsername } from '../utils/generateUsername';
|
||||
import { User } from '../../src/models';
|
||||
import { IGenerateJoinCoop, IGeneratedDocument, IIndividualData } from 'coopdoc-generator-ts';
|
||||
import { IDocument, IJoinCooperative } from '../../src/types';
|
||||
import ecc from 'eosjs-ecc'
|
||||
import ecc from 'eosjs-ecc';
|
||||
import { admin, chairman, insertUsers, voskhod } from '../fixtures/user.fixture';
|
||||
import { installInitialCooperativeData } from '../fixtures/document.fixture';
|
||||
import { PrivateKey } from '@wharfkit/antelope';
|
||||
|
||||
setupTestDB();
|
||||
|
||||
describe('Проверка данных', () => {
|
||||
let newUser;
|
||||
|
||||
beforeEach(() => {
|
||||
const email = faker.internet.email().toLowerCase()
|
||||
beforeEach(async () => {
|
||||
const coopData = voskhod;
|
||||
|
||||
await insertUsers([coopData, chairman, admin]);
|
||||
await installInitialCooperativeData();
|
||||
|
||||
const email = faker.internet.email().toLowerCase();
|
||||
newUser = {
|
||||
email: email,
|
||||
password: 'password1',
|
||||
@@ -39,8 +47,8 @@ describe('Проверка данных', () => {
|
||||
describe('POST /v1/users/join-cooperative', () => {
|
||||
let newUser;
|
||||
|
||||
beforeEach(() => {
|
||||
const email = faker.internet.email().toLowerCase()
|
||||
beforeEach(async () => {
|
||||
const email = faker.internet.email().toLowerCase();
|
||||
newUser = {
|
||||
email: email,
|
||||
password: 'password1',
|
||||
@@ -66,7 +74,7 @@ describe('Проверка данных', () => {
|
||||
|
||||
expect(registeredUser.body.tokens.access.token).toBeDefined();
|
||||
|
||||
const dbUser = await User.findOne({username: newUser.username});
|
||||
const dbUser = await User.findOne({ username: newUser.username });
|
||||
expect(dbUser).toBeDefined();
|
||||
expect(dbUser?.status).toBe('created');
|
||||
});
|
||||
@@ -76,11 +84,11 @@ describe('Проверка данных', () => {
|
||||
|
||||
expect(registeredUser.body.tokens.access.token).toBeDefined();
|
||||
|
||||
const dbUser = await User.findOne({username: newUser.username});
|
||||
const dbUser = await User.findOne({ username: newUser.username });
|
||||
expect(dbUser).toBeDefined();
|
||||
expect(dbUser?.status).toBe('created');
|
||||
|
||||
const privateData = await dbUser?.getPrivateData() as IIndividualData;
|
||||
const privateData = (await dbUser?.getPrivateData()) as IIndividualData;
|
||||
|
||||
expect(privateData).toBeDefined();
|
||||
|
||||
@@ -89,11 +97,15 @@ describe('Проверка данных', () => {
|
||||
code: 'registrator',
|
||||
coopname: 'voskhod',
|
||||
username: newUser.username,
|
||||
signature: 'this is imaged signature'
|
||||
signature: 'this is imaged signature',
|
||||
skip_save: false,
|
||||
};
|
||||
|
||||
let res = await request(app).post('/v1/data/generate').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(options);
|
||||
console.log("res.body: ", res.body)
|
||||
let res = await request(app)
|
||||
.post('/v1/documents/generate')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(options);
|
||||
// console.log('res.body: ', res.body);
|
||||
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
|
||||
@@ -108,11 +120,11 @@ describe('Проверка данных', () => {
|
||||
|
||||
expect(registeredUser.body.tokens.access.token).toBeDefined();
|
||||
|
||||
const dbUser = await User.findOne({username: newUser.username});
|
||||
const dbUser = await User.findOne({ username: newUser.username });
|
||||
expect(dbUser).toBeDefined();
|
||||
expect(dbUser?.status).toBe('created');
|
||||
|
||||
const privateData = await dbUser?.getPrivateData() as IIndividualData;
|
||||
const privateData = (await dbUser?.getPrivateData()) as IIndividualData;
|
||||
|
||||
expect(privateData).toBeDefined();
|
||||
|
||||
@@ -121,10 +133,14 @@ describe('Проверка данных', () => {
|
||||
code: 'registrator',
|
||||
coopname: 'voskhod',
|
||||
username: newUser.username,
|
||||
signature: 'this is imaged signature'
|
||||
signature: 'this is imaged signature',
|
||||
skip_save: false,
|
||||
};
|
||||
|
||||
let res = await request(app).post('/v1/data/generate').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(options);
|
||||
let res = await request(app)
|
||||
.post('/v1/documents/generate')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(options);
|
||||
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
|
||||
@@ -133,30 +149,38 @@ describe('Проверка данных', () => {
|
||||
expect(res.body.html).toBeDefined();
|
||||
expect(res.body.hash).toBeDefined();
|
||||
|
||||
let res_regenerated = await request(app).post('/v1/data/generate').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(res.body.meta);
|
||||
let res_regenerated = await request(app)
|
||||
.post('/v1/documents/generate')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(res.body.meta);
|
||||
|
||||
// console.log('res.body', res_regenerated.body);
|
||||
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
|
||||
expect(res.body.hash).toEqual(res_regenerated.body.hash);
|
||||
});
|
||||
|
||||
|
||||
test('should return 500 error if signature is invalid', async () => {
|
||||
const registeredUser = await request(app).post('/v1/users').send(newUser).expect(httpStatus.CREATED);
|
||||
expect(registeredUser.body.tokens.access.token).toBeDefined();
|
||||
const dbUser = await User.findOne({username: newUser.username});
|
||||
const dbUser = await User.findOne({ username: newUser.username });
|
||||
expect(dbUser).toBeDefined();
|
||||
expect(dbUser?.status).toBe('created');
|
||||
const privateData = await dbUser?.getPrivateData() as IIndividualData;
|
||||
const privateData = (await dbUser?.getPrivateData()) as IIndividualData;
|
||||
expect(privateData).toBeDefined();
|
||||
const options: IGenerateJoinCoop = {
|
||||
action: 'joincoop',
|
||||
code: 'registrator',
|
||||
coopname: 'voskhod',
|
||||
username: newUser.username,
|
||||
signature: 'any imaged signature'
|
||||
signature: 'any imaged signature',
|
||||
skip_save: false,
|
||||
};
|
||||
let res = await request(app).post('/v1/data/generate').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(options);
|
||||
let res = await request(app)
|
||||
.post('/v1/documents/generate')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(options);
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
|
||||
const generatedDocument: IGeneratedDocument = res.body;
|
||||
@@ -170,36 +194,47 @@ describe('Проверка данных', () => {
|
||||
|
||||
const joinCoopData: IJoinCooperative = {
|
||||
username: newUser.username,
|
||||
document: signedDocument
|
||||
statement: signedDocument,
|
||||
};
|
||||
|
||||
let joincoop_result = await request(app).post('/v1/users/join-cooperative').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(joinCoopData);
|
||||
console.log(joincoop_result.body);
|
||||
let joincoop_result = await request(app)
|
||||
.post('/v1/users/join-cooperative')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(joinCoopData);
|
||||
// console.log(joincoop_result.body);
|
||||
expect(joincoop_result.status).toBe(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
});
|
||||
|
||||
|
||||
test('should return 400 error if public_key is mismatch', async () => {
|
||||
const registeredUser = await request(app).post('/v1/users').send(newUser).expect(httpStatus.CREATED);
|
||||
expect(registeredUser.body.tokens.access.token).toBeDefined();
|
||||
const dbUser = await User.findOne({username: newUser.username});
|
||||
const dbUser = await User.findOne({ username: newUser.username });
|
||||
expect(dbUser).toBeDefined();
|
||||
expect(dbUser?.status).toBe('created');
|
||||
const privateData = await dbUser?.getPrivateData() as IIndividualData;
|
||||
const privateData = (await dbUser?.getPrivateData()) as IIndividualData;
|
||||
expect(privateData).toBeDefined();
|
||||
|
||||
const options: IGenerateJoinCoop = {
|
||||
action: 'joincoop',
|
||||
code: 'registrator',
|
||||
coopname: 'voskhod',
|
||||
username: newUser.username,
|
||||
signature: 'any imaged signature'
|
||||
signature: 'any imaged signature',
|
||||
skip_save: false,
|
||||
};
|
||||
let res = await request(app).post('/v1/data/generate').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(options);
|
||||
let res = await request(app)
|
||||
.post('/v1/documents/generate')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(options);
|
||||
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
|
||||
const generatedDocument: IGeneratedDocument = res.body;
|
||||
|
||||
const sign = await ecc.sign(generatedDocument.hash, '5JA8KCpXbCfWA9fS4zHKgcBPbnNaRha8iKHhZLV7ks9Gs3LenpU')
|
||||
// const sign = await ecc.sign(generatedDocument.hash, '5JA8KCpXbCfWA9fS4zHKgcBPbnNaRha8iKHhZLV7ks9Gs3LenpU');
|
||||
const key = '5JA8KCpXbCfWA9fS4zHKgcBPbnNaRha8iKHhZLV7ks9Gs3LenpU';
|
||||
const wif = await PrivateKey.fromString(key);
|
||||
const sign = await wif.signDigest(generatedDocument.hash).toString();
|
||||
|
||||
const signedDocument: IDocument = {
|
||||
hash: generatedDocument.hash,
|
||||
@@ -210,25 +245,27 @@ describe('Проверка данных', () => {
|
||||
|
||||
const joinCoopData: IJoinCooperative = {
|
||||
username: newUser.username,
|
||||
document: signedDocument
|
||||
statement: signedDocument,
|
||||
};
|
||||
|
||||
let joincoop_result = await request(app).post('/v1/users/join-cooperative').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(joinCoopData);
|
||||
console.log(joincoop_result.body);
|
||||
let joincoop_result = await request(app)
|
||||
.post('/v1/users/join-cooperative')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(joinCoopData);
|
||||
// console.log(joincoop_result.body);
|
||||
expect(joincoop_result.status).toBe(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
|
||||
test('should join the cooperative', async () => {
|
||||
const registeredUser = await request(app).post('/v1/users').send(newUser).expect(httpStatus.CREATED);
|
||||
|
||||
expect(registeredUser.body.tokens.access.token).toBeDefined();
|
||||
|
||||
const dbUser = await User.findOne({username: newUser.username});
|
||||
const dbUser = await User.findOne({ username: newUser.username });
|
||||
expect(dbUser).toBeDefined();
|
||||
expect(dbUser?.status).toBe('created');
|
||||
|
||||
const privateData = await dbUser?.getPrivateData() as IIndividualData;
|
||||
const privateData = (await dbUser?.getPrivateData()) as IIndividualData;
|
||||
|
||||
expect(privateData).toBeDefined();
|
||||
|
||||
@@ -237,10 +274,14 @@ describe('Проверка данных', () => {
|
||||
code: 'registrator',
|
||||
coopname: 'voskhod',
|
||||
username: newUser.username,
|
||||
signature: 'this is imaged signature'
|
||||
signature: 'this is imaged signature',
|
||||
skip_save: false,
|
||||
};
|
||||
|
||||
let res = await request(app).post('/v1/data/generate').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(options);
|
||||
let res = await request(app)
|
||||
.post('/v1/documents/generate')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(options);
|
||||
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
|
||||
@@ -249,7 +290,10 @@ describe('Проверка данных', () => {
|
||||
expect(res.body.html).toBeDefined();
|
||||
expect(res.body.hash).toBeDefined();
|
||||
|
||||
let res_regenerated = await request(app).post('/v1/data/generate').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(res.body.meta);
|
||||
let res_regenerated = await request(app)
|
||||
.post('/v1/documents/generate')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(res.body.meta);
|
||||
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
|
||||
@@ -257,7 +301,9 @@ describe('Проверка данных', () => {
|
||||
|
||||
const generatedDocument: IGeneratedDocument = res.body;
|
||||
|
||||
const sign = await ecc.sign(generatedDocument.hash, '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3')
|
||||
const key = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3';
|
||||
const wif = await PrivateKey.fromString(key);
|
||||
const sign = await wif.signDigest(generatedDocument.hash).toString();
|
||||
|
||||
const signedDocument: IDocument = {
|
||||
hash: generatedDocument.hash,
|
||||
@@ -268,93 +314,25 @@ describe('Проверка данных', () => {
|
||||
|
||||
const joinCoopData: IJoinCooperative = {
|
||||
username: newUser.username,
|
||||
document: signedDocument
|
||||
statement: signedDocument,
|
||||
};
|
||||
|
||||
let joincoop_result = await request(app).post('/v1/users/join-cooperative').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(joinCoopData);
|
||||
console.log(joincoop_result.body);
|
||||
let joincoop_result = await request(app)
|
||||
.post('/v1/users/join-cooperative')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(joinCoopData);
|
||||
// console.log(joincoop_result.body);
|
||||
expect(joincoop_result.status).toBe(httpStatus.OK);
|
||||
|
||||
const dbUser2 = await User.findOne({username: newUser.username});
|
||||
const dbUser2 = await User.findOne({ username: newUser.username });
|
||||
expect(dbUser2).toBeDefined();
|
||||
expect(dbUser2?.status).toBe('joined');
|
||||
expect(dbUser2?.statement).toBeDefined()
|
||||
console.log(dbUser2);
|
||||
expect(dbUser2?.statement).toBeDefined();
|
||||
// console.log(dbUser2);
|
||||
});
|
||||
|
||||
|
||||
test('получаем платежный ордер на вступление', async () => {
|
||||
const registeredUser = await request(app).post('/v1/users').send(newUser).expect(httpStatus.CREATED);
|
||||
|
||||
expect(registeredUser.body.tokens.access.token).toBeDefined();
|
||||
|
||||
const dbUser = await User.findOne({username: newUser.username});
|
||||
expect(dbUser).toBeDefined();
|
||||
expect(dbUser?.status).toBe('created');
|
||||
|
||||
const privateData = await dbUser?.getPrivateData() as IIndividualData;
|
||||
|
||||
expect(privateData).toBeDefined();
|
||||
|
||||
const options: IGenerateJoinCoop = {
|
||||
action: 'joincoop',
|
||||
code: 'registrator',
|
||||
coopname: 'voskhod',
|
||||
username: newUser.username,
|
||||
signature: 'this is imaged signature'
|
||||
};
|
||||
|
||||
let res = await request(app).post('/v1/data/generate').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(options);
|
||||
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
|
||||
expect(res.body.meta).toBeDefined();
|
||||
expect(res.body.binary).toBeDefined();
|
||||
expect(res.body.html).toBeDefined();
|
||||
expect(res.body.hash).toBeDefined();
|
||||
|
||||
let res_regenerated = await request(app).post('/v1/data/generate').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(res.body.meta);
|
||||
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
|
||||
expect(res.body.hash).toEqual(res_regenerated.body.hash);
|
||||
|
||||
const generatedDocument: IGeneratedDocument = res.body;
|
||||
|
||||
const sign = await ecc.sign(generatedDocument.hash, '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3')
|
||||
|
||||
const signedDocument: IDocument = {
|
||||
hash: generatedDocument.hash,
|
||||
meta: generatedDocument.meta,
|
||||
signature: sign,
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
};
|
||||
|
||||
const joinCoopData: IJoinCooperative = {
|
||||
username: newUser.username,
|
||||
document: signedDocument
|
||||
};
|
||||
|
||||
let joincoop_result = await request(app).post('/v1/users/join-cooperative').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send(joinCoopData);
|
||||
console.log(joincoop_result.body);
|
||||
expect(joincoop_result.status).toBe(httpStatus.OK);
|
||||
|
||||
const dbUser2 = await User.findOne({username: newUser.username});
|
||||
expect(dbUser2).toBeDefined();
|
||||
expect(dbUser2?.status).toBe('joined');
|
||||
expect(dbUser2?.statement).toBeDefined()
|
||||
console.log(dbUser2);
|
||||
|
||||
const initialPayment = await request(app).post('/v1/orders/initial').set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`).send({username: newUser.username})
|
||||
console.log('result: ', initialPayment.body)
|
||||
|
||||
expect(initialPayment.status).toBe(httpStatus.CREATED)
|
||||
expect(initialPayment.body?.confirmation_token).toBeDefined()
|
||||
expect(initialPayment.body?.order_id).toBeDefined()
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// ---
|
||||
// test('регистрируем пользователя в блокчейне', async () => {
|
||||
// await request(app)
|
||||
// .post('/v1/auth/register')
|
||||
@@ -365,9 +343,9 @@ describe('Проверка данных', () => {
|
||||
|
||||
// test('should return 201 and successfully register user if request data is ok', async () => {
|
||||
// await request(app)
|
||||
// .post('/v1/data/generate')
|
||||
// .post('/v1/documents/generate')
|
||||
// // .set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
// .send(newUser)
|
||||
// .expect(httpStatus.BAD_REQUEST);
|
||||
// })
|
||||
})
|
||||
});
|
||||
|
||||
@@ -0,0 +1,540 @@
|
||||
import app from '../../src/app';
|
||||
import request from 'supertest';
|
||||
import httpStatus from 'http-status';
|
||||
import { setupTestDB } from '../utils/setupTestDB';
|
||||
import faker from 'faker';
|
||||
import { generateUsername } from '../utils/generateUsername';
|
||||
import { User } from '../../src/models';
|
||||
import { IGenerateJoinCoop, IGeneratedDocument, IIndividualData } from 'coopdoc-generator-ts';
|
||||
import {
|
||||
IBankAccount,
|
||||
IDeletePaymentMethod,
|
||||
IDocument,
|
||||
IJoinCooperative,
|
||||
ISavePaymentMethod,
|
||||
ISbpDetails,
|
||||
} from '../../src/types';
|
||||
import ecc from 'eosjs-ecc';
|
||||
import { admin, chairman, insertUsers, userOne, userTwo, voskhod } from '../fixtures/user.fixture';
|
||||
import { adminAccessToken, userOneAccessToken, userTwoAccessToken } from '../fixtures/token.fixture';
|
||||
import { fixtureAction, insertAction, insertActions, installInitialCooperativeData } from '../fixtures/document.fixture';
|
||||
import { Cooperative, SovietContract } from 'cooptypes';
|
||||
import { participantOne } from '../fixtures/participant.fixture';
|
||||
import { PrivateKey } from '@wharfkit/antelope';
|
||||
import exp from 'constants';
|
||||
|
||||
const public_key = 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV';
|
||||
|
||||
setupTestDB();
|
||||
|
||||
describe('Проверка получения документов', () => {
|
||||
let sbpPaymentData: ISavePaymentMethod;
|
||||
let bankPaymentData: ISavePaymentMethod;
|
||||
|
||||
beforeEach(() => {
|
||||
sbpPaymentData = {
|
||||
username: '',
|
||||
method_id: 1,
|
||||
method_type: 'sbp',
|
||||
data: { phone: '+7900-888-988-00' } as ISbpDetails,
|
||||
};
|
||||
|
||||
bankPaymentData = {
|
||||
username: '',
|
||||
method_id: 2,
|
||||
method_type: 'bank_transfer',
|
||||
data: {
|
||||
account_number: '40817810099910004312',
|
||||
currency: 'RUB',
|
||||
card_number: '1234567890123456',
|
||||
bank_name: 'Sberbank',
|
||||
details: {
|
||||
bik: '123456789',
|
||||
corr: '30101810400000000225',
|
||||
kpp: '123456789',
|
||||
},
|
||||
} as IBankAccount,
|
||||
};
|
||||
});
|
||||
|
||||
describe('GET /v1/payments/initial', () => {
|
||||
test('получаем платежный ордер на вступление', async () => {
|
||||
const coopData = voskhod;
|
||||
|
||||
await insertUsers([coopData, chairman, admin]);
|
||||
await installInitialCooperativeData();
|
||||
|
||||
const email = faker.internet.email().toLowerCase();
|
||||
const newUser = {
|
||||
email: email,
|
||||
password: 'password1',
|
||||
role: 'user',
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
username: generateUsername(),
|
||||
referer: '',
|
||||
type: 'individual',
|
||||
individual_data: {
|
||||
first_name: faker.name.firstName(),
|
||||
last_name: faker.name.lastName(),
|
||||
middle_name: '',
|
||||
birthdate: '2023-04-01',
|
||||
phone: '+1234567890',
|
||||
email: email,
|
||||
full_address: 'Russia, Moscow, Tverskaya street, 10',
|
||||
},
|
||||
};
|
||||
|
||||
const registeredUser = await request(app).post('/v1/users').send(newUser).expect(httpStatus.CREATED);
|
||||
|
||||
expect(registeredUser.body.tokens.access.token).toBeDefined();
|
||||
|
||||
const dbUser = await User.findOne({ username: newUser.username });
|
||||
expect(dbUser).toBeDefined();
|
||||
expect(dbUser?.status).toBe('created');
|
||||
|
||||
const privateData = (await dbUser?.getPrivateData()) as IIndividualData;
|
||||
|
||||
expect(privateData).toBeDefined();
|
||||
|
||||
const options: IGenerateJoinCoop = {
|
||||
action: 'joincoop',
|
||||
code: 'registrator',
|
||||
coopname: 'voskhod',
|
||||
username: newUser.username,
|
||||
signature: 'this is imaged signature',
|
||||
skip_save: false,
|
||||
};
|
||||
|
||||
let res = await request(app)
|
||||
.post('/v1/documents/generate')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(options);
|
||||
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
|
||||
expect(res.body.meta).toBeDefined();
|
||||
expect(res.body.binary).toBeDefined();
|
||||
expect(res.body.html).toBeDefined();
|
||||
expect(res.body.hash).toBeDefined();
|
||||
|
||||
let res_regenerated = await request(app)
|
||||
.post('/v1/documents/generate')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(res.body.meta);
|
||||
|
||||
expect(res.status).toBe(httpStatus.CREATED);
|
||||
|
||||
expect(res.body.hash).toEqual(res_regenerated.body.hash);
|
||||
|
||||
const generatedDocument: IGeneratedDocument = res.body;
|
||||
|
||||
const key = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3';
|
||||
const wif = await PrivateKey.fromString(key);
|
||||
const sign = await wif.signDigest(generatedDocument.hash).toString();
|
||||
|
||||
const signedDocument: IDocument = {
|
||||
hash: generatedDocument.hash,
|
||||
meta: generatedDocument.meta,
|
||||
signature: sign,
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
};
|
||||
|
||||
const joinCoopData: IJoinCooperative = {
|
||||
username: newUser.username,
|
||||
statement: signedDocument,
|
||||
};
|
||||
|
||||
const joincoop_result = await request(app)
|
||||
.post('/v1/users/join-cooperative')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send(joinCoopData);
|
||||
// console.log(joincoop_result.body);
|
||||
expect(joincoop_result.status).toBe(httpStatus.OK);
|
||||
|
||||
const dbUser2 = await User.findOne({ username: newUser.username });
|
||||
expect(dbUser2).toBeDefined();
|
||||
expect(dbUser2?.status).toBe('joined');
|
||||
expect(dbUser2?.statement).toBeDefined();
|
||||
// console.log(dbUser2);
|
||||
|
||||
const initialPayment = await request(app)
|
||||
.post('/v1/payments/initial')
|
||||
.set('Authorization', `Bearer ${registeredUser.body.tokens.access.token}`)
|
||||
.send({ provider: 'yookassa' });
|
||||
|
||||
expect(initialPayment.status).toBe(httpStatus.CREATED);
|
||||
expect(initialPayment.body?.details?.token).toBeDefined();
|
||||
expect(initialPayment.body?.order_id).toBeDefined();
|
||||
expect(initialPayment.body?.provider).toBe('yookassa');
|
||||
});
|
||||
});
|
||||
describe('POST /v1/payments/methods/:username + add', () => {
|
||||
test('401 при добавлении реквизитов самому себе без авторизации', async () => {
|
||||
await insertUsers([userOne]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
const documents = await request(app).post(`/v1/payments/methods/${userOne.username}/add`).send(sbpPaymentData);
|
||||
|
||||
expect(documents.status).toBe(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('403 при добавлении реквизитов кому-то со своей авторизацией', async () => {
|
||||
await insertUsers([userOne, userTwo]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
const documents = await request(app)
|
||||
.post(`/v1/payments/methods/${userTwo.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData);
|
||||
|
||||
expect(documents.status).toBe(httpStatus.FORBIDDEN);
|
||||
});
|
||||
|
||||
test('200 при добавлении реквизитов СБП самому себе и получение их', async () => {
|
||||
await insertUsers([userOne]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
const documents = await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData);
|
||||
|
||||
expect(documents.status).toBe(httpStatus.OK);
|
||||
|
||||
const list = await request(app)
|
||||
.get(`/v1/payments/methods/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send();
|
||||
|
||||
expect(list.status).toBe(httpStatus.OK);
|
||||
});
|
||||
|
||||
test('200 при добавлении реквизитов СБП админом кому-то и получение их', async () => {
|
||||
await insertUsers([userOne, admin]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
const documents = await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send(sbpPaymentData);
|
||||
|
||||
expect(documents.status).toBe(httpStatus.OK);
|
||||
|
||||
const list = await request(app)
|
||||
.get(`/v1/payments/methods/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send();
|
||||
|
||||
expect(list.status).toBe(httpStatus.OK);
|
||||
});
|
||||
|
||||
test('200 при извлечении списка всех реквизитов админом', async () => {
|
||||
await insertUsers([voskhod, userOne, admin]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
const documents = await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData);
|
||||
|
||||
expect(documents.status).toBe(httpStatus.OK);
|
||||
|
||||
const list = await request(app).get(`/v1/payments/methods`).set('Authorization', `Bearer ${adminAccessToken}`).send();
|
||||
expect(list.status).toBe(httpStatus.OK);
|
||||
|
||||
expect(list.body).toEqual({
|
||||
results: expect.any(Array),
|
||||
page: 1,
|
||||
limit: 100,
|
||||
totalPages: 1,
|
||||
totalResults: 2,
|
||||
});
|
||||
});
|
||||
|
||||
test('200 при добавлении СБП и банковских реквизитов самому себе и получение их', async () => {
|
||||
await insertUsers([userOne]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
bankPaymentData.username = userOne.username;
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(bankPaymentData)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
const list = await request(app)
|
||||
.get(`/v1/payments/methods/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send();
|
||||
|
||||
expect(list.status).toBe(httpStatus.OK);
|
||||
|
||||
expect(list.body).toEqual({
|
||||
results: expect.any(Array),
|
||||
page: 1,
|
||||
limit: 100,
|
||||
totalPages: 1,
|
||||
totalResults: 2,
|
||||
});
|
||||
});
|
||||
|
||||
test('403 при получении чужого списка платежных методов не админом', async () => {
|
||||
await insertUsers([userOne, userTwo]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
bankPaymentData.username = userTwo.username;
|
||||
|
||||
let res = await request(app)
|
||||
.post(`/v1/payments/methods/${userTwo.username}/add`)
|
||||
.set('Authorization', `Bearer ${userTwoAccessToken}`)
|
||||
.send(bankPaymentData);
|
||||
|
||||
expect(res.status).toBe(httpStatus.OK);
|
||||
|
||||
const list = await request(app)
|
||||
.get(`/v1/payments/methods/${userTwo.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send();
|
||||
|
||||
expect(list.status).toBe(httpStatus.FORBIDDEN);
|
||||
});
|
||||
|
||||
test('200 при получение двух методов двух пользователей админом', async () => {
|
||||
await insertUsers([userOne, userTwo, admin]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
bankPaymentData.username = userTwo.username;
|
||||
|
||||
let res = await request(app)
|
||||
.post(`/v1/payments/methods/${userTwo.username}/add`)
|
||||
.set('Authorization', `Bearer ${userTwoAccessToken}`)
|
||||
.send(bankPaymentData);
|
||||
|
||||
expect(res.status).toBe(httpStatus.OK);
|
||||
|
||||
const list = await request(app).get(`/v1/payments/methods/`).set('Authorization', `Bearer ${adminAccessToken}`).send();
|
||||
|
||||
expect(list.status).toBe(httpStatus.OK);
|
||||
|
||||
expect(list.body).toEqual({
|
||||
results: expect.any(Array),
|
||||
page: 1,
|
||||
limit: 100,
|
||||
totalPages: 1,
|
||||
totalResults: 2,
|
||||
});
|
||||
});
|
||||
|
||||
test('200 при обновлении собственных реквизитов после добавления пользователем', async () => {
|
||||
await insertUsers([userOne, userTwo, admin]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
const sbpPaymentData2 = sbpPaymentData;
|
||||
|
||||
sbpPaymentData2.data = { phone: '111111111' };
|
||||
|
||||
let res = await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData2);
|
||||
|
||||
expect(res.status).toBe(httpStatus.OK);
|
||||
|
||||
const list = await request(app)
|
||||
.get(`/v1/payments/methods/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send();
|
||||
|
||||
expect(list.status).toBe(httpStatus.OK);
|
||||
// console.log(list.body.results[0]);
|
||||
|
||||
expect(list.body).toEqual({
|
||||
results: expect.any(Array),
|
||||
page: 1,
|
||||
limit: 100,
|
||||
totalPages: 1,
|
||||
totalResults: 1,
|
||||
});
|
||||
|
||||
expect(list.body.results[0].data.phone).toBe(sbpPaymentData2.data.phone);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /v1/payments/methods/:username/delete', () => {
|
||||
test('200 при удалении собственных реквизитов после добавления', async () => {
|
||||
await insertUsers([userOne, userTwo, admin]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
const dataForDelete: IDeletePaymentMethod = {
|
||||
method_id: sbpPaymentData.method_id,
|
||||
};
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/delete`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(dataForDelete)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
const list = await request(app)
|
||||
.get(`/v1/payments/methods/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send();
|
||||
|
||||
expect(list.status).toBe(httpStatus.OK);
|
||||
// console.log('list.body: ', list.body);
|
||||
|
||||
expect(list.body).toEqual({
|
||||
results: expect.any(Array),
|
||||
page: 1,
|
||||
limit: 100,
|
||||
totalPages: 0,
|
||||
totalResults: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('403 при удалении чужих реквизитов', async () => {
|
||||
await insertUsers([userOne, userTwo, admin]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
const dataForDelete: IDeletePaymentMethod = {
|
||||
method_id: sbpPaymentData.method_id,
|
||||
};
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/delete`)
|
||||
.set('Authorization', `Bearer ${userTwoAccessToken}`)
|
||||
.send(dataForDelete)
|
||||
.expect(httpStatus.FORBIDDEN);
|
||||
});
|
||||
|
||||
test('200 при удалении нескольких версий собственных реквизитов под одним method_id', async () => {
|
||||
await insertUsers([userOne, userTwo, admin]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
const dataForDelete: IDeletePaymentMethod = {
|
||||
method_id: sbpPaymentData.method_id,
|
||||
};
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/delete`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(dataForDelete)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
const list = await request(app)
|
||||
.get(`/v1/payments/methods/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send();
|
||||
|
||||
expect(list.status).toBe(httpStatus.OK);
|
||||
// console.log('list.body: ', list.body);
|
||||
|
||||
expect(list.body).toEqual({
|
||||
results: expect.any(Array),
|
||||
page: 1,
|
||||
limit: 100,
|
||||
totalPages: 0,
|
||||
totalResults: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('200 при удалении чужих реквизитов админом', async () => {
|
||||
await insertUsers([userOne, userTwo, admin]);
|
||||
|
||||
sbpPaymentData.username = userOne.username;
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/add`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(sbpPaymentData)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
const dataForDelete: IDeletePaymentMethod = {
|
||||
method_id: sbpPaymentData.method_id,
|
||||
};
|
||||
|
||||
await request(app)
|
||||
.post(`/v1/payments/methods/${userOne.username}/delete`)
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send(dataForDelete)
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
const list = await request(app)
|
||||
.get(`/v1/payments/methods/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send();
|
||||
|
||||
expect(list.status).toBe(httpStatus.OK);
|
||||
// console.log('list.body: ', list.body);
|
||||
|
||||
expect(list.body).toEqual({
|
||||
results: expect.any(Array),
|
||||
page: 1,
|
||||
limit: 100,
|
||||
totalPages: 0,
|
||||
totalResults: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -53,17 +53,6 @@ describe('User routes', () => {
|
||||
|
||||
expect(res.body.user).not.toHaveProperty('password');
|
||||
|
||||
// expect(res.body.user).toEqual({
|
||||
// email: newUser.email,
|
||||
// role: newUser.role,
|
||||
// is_email_verified: false,
|
||||
// username: newUser.username,
|
||||
// public_key: newUser.public_key,
|
||||
// referer: newUser.referer,
|
||||
// is_registered: false,
|
||||
// type: 'individual',
|
||||
// });
|
||||
|
||||
const dbUser = await User.findOne({ username: newUser.username });
|
||||
expect(dbUser).toBeDefined();
|
||||
expect(dbUser?.password).not.toBe(newUser.password);
|
||||
@@ -73,16 +62,12 @@ describe('User routes', () => {
|
||||
expect(privateData.first_name).toEqual(newUser.individual_data.first_name);
|
||||
expect(privateData.last_name).toEqual(newUser.individual_data.last_name);
|
||||
|
||||
// const validated = IndividualDataSchema.validate(privateData)
|
||||
// console.log(validated)
|
||||
// expect(validated).toBeTruthy()
|
||||
|
||||
expect(dbUser?.toObject()).toMatchObject({ email: newUser.email, role: newUser.role, is_email_verified: false });
|
||||
});
|
||||
|
||||
test('should be able to create an admin as well', async () => {
|
||||
await insertUsers([admin]);
|
||||
newUser.role = 'admin';
|
||||
newUser.role = 'member';
|
||||
|
||||
const res = await request(app)
|
||||
.post('/v1/users')
|
||||
@@ -93,10 +78,10 @@ describe('User routes', () => {
|
||||
expect(res.body.user).toBeDefined();
|
||||
expect(res.body.tokens).toBeDefined();
|
||||
|
||||
expect(res.body.user.role).toBe('admin');
|
||||
expect(res.body.user.role).toBe('member');
|
||||
|
||||
const dbUser = await User.findOne({ username: newUser.username });
|
||||
expect(dbUser?.role).toBe('admin');
|
||||
expect(dbUser?.role).toBe('member');
|
||||
});
|
||||
|
||||
test('успешная регистрация без токена доступа', async () => {
|
||||
@@ -185,7 +170,7 @@ describe('User routes', () => {
|
||||
|
||||
const res = await request(app).get('/v1/users').set('Authorization', `Bearer ${adminAccessToken}`).send();
|
||||
|
||||
console.log(res.body);
|
||||
// console.log(res.body);
|
||||
expect(res.status).toBe(httpStatus.OK);
|
||||
|
||||
expect(res.body).toEqual({
|
||||
@@ -198,23 +183,25 @@ describe('User routes', () => {
|
||||
expect(res.body.results).toHaveLength(3);
|
||||
|
||||
expect(res.body.results[0]).toEqual({
|
||||
username: userOne.username,
|
||||
id: userOne._id.toString(),
|
||||
email: userOne.email,
|
||||
role: userOne.role,
|
||||
is_email_verified: userOne.is_email_verified,
|
||||
has_account: false,
|
||||
is_registered: false,
|
||||
private_data: {},
|
||||
is_registered: userOne.is_registered,
|
||||
private_data: {
|
||||
...userOne.individual_data,
|
||||
_id: expect.any(String),
|
||||
block_num: 0,
|
||||
_created_at: expect.any(String),
|
||||
},
|
||||
public_key: userOne.public_key,
|
||||
referer: '',
|
||||
statement: {
|
||||
hash: '',
|
||||
public_key: '',
|
||||
signature: '',
|
||||
},
|
||||
status: 'created',
|
||||
statement: userOne.statement,
|
||||
status: userOne.status,
|
||||
message: userOne.message,
|
||||
type: userOne.type,
|
||||
username: userOne.username,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -232,7 +219,7 @@ describe('User routes', () => {
|
||||
expect(res.status).toBe(httpStatus.FORBIDDEN);
|
||||
});
|
||||
|
||||
test('should correctly apply filter on name field', async () => {
|
||||
test('should correctly apply filter on username field', async () => {
|
||||
await insertUsers([userOne, userTwo, admin]);
|
||||
|
||||
const res = await request(app)
|
||||
@@ -242,7 +229,7 @@ describe('User routes', () => {
|
||||
.send()
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
console.log('res.body', res.body);
|
||||
// console.log('res.body', res.body);
|
||||
|
||||
expect(res.body).toEqual({
|
||||
results: expect.any(Array),
|
||||
@@ -262,8 +249,9 @@ describe('User routes', () => {
|
||||
.get('/v1/users')
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.query({ role: 'user' })
|
||||
.send()
|
||||
.expect(httpStatus.OK);
|
||||
.send();
|
||||
|
||||
expect(res.status).toBe(httpStatus.OK);
|
||||
|
||||
expect(res.body).toEqual({
|
||||
results: expect.any(Array),
|
||||
@@ -272,6 +260,7 @@ describe('User routes', () => {
|
||||
totalPages: 1,
|
||||
totalResults: 2,
|
||||
});
|
||||
|
||||
expect(res.body.results).toHaveLength(2);
|
||||
expect(res.body.results[0].id).toBe(userOne._id.toString());
|
||||
expect(res.body.results[1].id).toBe(userTwo._id.toString());
|
||||
@@ -329,7 +318,7 @@ describe('User routes', () => {
|
||||
const res = await request(app)
|
||||
.get('/v1/users')
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.query({ sortBy: 'role:desc,name:asc' })
|
||||
.query({ sortBy: 'role:desc,username:asc' })
|
||||
.send()
|
||||
.expect(httpStatus.OK);
|
||||
|
||||
@@ -520,7 +509,7 @@ describe('User routes', () => {
|
||||
// });
|
||||
|
||||
describe('PATCH /v1/users/:userId', () => {
|
||||
test('should return 200 and successfully update user if data is ok', async () => {
|
||||
test('should return 403 and fail update user if not admin', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const updateBody = {
|
||||
email: faker.internet.email().toLowerCase(),
|
||||
@@ -530,18 +519,27 @@ describe('User routes', () => {
|
||||
const res = await request(app)
|
||||
.patch(`/v1/users/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.send(updateBody)
|
||||
.expect(httpStatus.OK);
|
||||
.send(updateBody);
|
||||
|
||||
expect(res.status).toBe(httpStatus.FORBIDDEN);
|
||||
});
|
||||
|
||||
test('should return 200 and successfully update user if data is ok and is admin', async () => {
|
||||
await insertUsers([userOne, admin]);
|
||||
const updateBody = {
|
||||
email: faker.internet.email().toLowerCase(),
|
||||
password: 'newPassword1',
|
||||
};
|
||||
|
||||
const res = await request(app)
|
||||
.patch(`/v1/users/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send(updateBody);
|
||||
|
||||
expect(res.status).toBe(httpStatus.OK);
|
||||
|
||||
expect(res.body).not.toHaveProperty('password');
|
||||
|
||||
// expect(res.body).toEqual({
|
||||
// id: userOne._id.toString(),
|
||||
// email: updateBody.email,
|
||||
// role: 'user',
|
||||
// is_email_verified: false,
|
||||
// });
|
||||
|
||||
const dbUser = await User.findById(userOne._id);
|
||||
expect(dbUser).toBeDefined();
|
||||
expect(dbUser?.password).not.toBe(updateBody.password);
|
||||
@@ -550,14 +548,20 @@ describe('User routes', () => {
|
||||
|
||||
test('should return 401 error if access token is missing', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const updateBody = { name: faker.name.findName() };
|
||||
const updateBody = {
|
||||
email: faker.internet.email().toLowerCase(),
|
||||
password: 'newPassword1',
|
||||
};
|
||||
|
||||
await request(app).patch(`/v1/users/${userOne.username}`).send(updateBody).expect(httpStatus.UNAUTHORIZED);
|
||||
});
|
||||
|
||||
test('should return 403 if user is updating another user', async () => {
|
||||
await insertUsers([userOne, userTwo]);
|
||||
const updateBody = { username: faker.name.findName() };
|
||||
const updateBody = {
|
||||
email: faker.internet.email().toLowerCase(),
|
||||
password: 'newPassword1',
|
||||
};
|
||||
|
||||
await request(app)
|
||||
.patch(`/v1/users/${userTwo.username}`)
|
||||
@@ -568,7 +572,10 @@ describe('User routes', () => {
|
||||
|
||||
test('should return 200 and successfully update user if admin is updating another user', async () => {
|
||||
await insertUsers([userOne, admin]);
|
||||
const updateBody = { name: faker.name.findName() };
|
||||
const updateBody = {
|
||||
email: faker.internet.email().toLowerCase(),
|
||||
password: 'newPassword1',
|
||||
};
|
||||
|
||||
await request(app)
|
||||
.patch(`/v1/users/${userOne.username}`)
|
||||
@@ -579,7 +586,10 @@ describe('User routes', () => {
|
||||
|
||||
test('should return 404 if admin is updating another user that is not found', async () => {
|
||||
await insertUsers([admin]);
|
||||
const updateBody = { name: faker.name.findName() };
|
||||
const updateBody = {
|
||||
email: faker.internet.email().toLowerCase(),
|
||||
password: 'newPassword1',
|
||||
};
|
||||
|
||||
await request(app)
|
||||
.patch(`/v1/users/${userOne.username}`)
|
||||
@@ -589,45 +599,45 @@ describe('User routes', () => {
|
||||
});
|
||||
|
||||
test('should return 400 if email is invalid', async () => {
|
||||
await insertUsers([userOne]);
|
||||
const updateBody = { email: 'invalidEmail' };
|
||||
await insertUsers([userOne, admin]);
|
||||
const updateBody = { email: 'invalidEmail', password: 'GoodPassword!23' };
|
||||
|
||||
await request(app)
|
||||
.patch(`/v1/users/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send(updateBody)
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 400 if email is already taken', async () => {
|
||||
await insertUsers([userOne, userTwo]);
|
||||
const updateBody = { email: userTwo.email };
|
||||
await insertUsers([userOne, userTwo, admin]);
|
||||
const updateBody = { email: userTwo.email, password: 'GoodPassword!23' };
|
||||
|
||||
await request(app)
|
||||
.patch(`/v1/users/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send(updateBody)
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 400 if password length is less than 8 characters', async () => {
|
||||
await insertUsers([userOne]);
|
||||
await insertUsers([userOne, admin]);
|
||||
const updateBody = { password: 'passwo1' };
|
||||
|
||||
await request(app)
|
||||
.patch(`/v1/users/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send(updateBody)
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
test('should return 400 if password does not contain both letters and numbers', async () => {
|
||||
await insertUsers([userOne]);
|
||||
await insertUsers([userOne, admin]);
|
||||
const updateBody = { password: 'password' };
|
||||
|
||||
await request(app)
|
||||
.patch(`/v1/users/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send(updateBody)
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
|
||||
@@ -635,43 +645,32 @@ describe('User routes', () => {
|
||||
|
||||
await request(app)
|
||||
.patch(`/v1/users/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send(updateBody)
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
// test('should not return 400 if email is my email', async () => {
|
||||
// await insertUsers([userOne]);
|
||||
// const updateBody = { email: userOne.email };
|
||||
test('should return 400 if email is my email then taken', async () => {
|
||||
await insertUsers([userOne, admin]);
|
||||
const updateBody = { email: userOne.email, password: 'veryStrongPassword!2' };
|
||||
|
||||
// await request(app)
|
||||
// .patch(`/v1/users/${userOne.username}`)
|
||||
// .set('Authorization', `Bearer ${userOneAccessToken}`)
|
||||
// .send(updateBody)
|
||||
// .expect(httpStatus.OK);
|
||||
// });
|
||||
await request(app)
|
||||
.patch(`/v1/users/${userOne.username}`)
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send(updateBody)
|
||||
.expect(httpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
// test('should return 400 error if userId is not a valid mongo id', async () => {
|
||||
// await insertUsers([admin]);
|
||||
test('should return 404 error if username is not found', async () => {
|
||||
await insertUsers([admin]);
|
||||
|
||||
// await request(app)
|
||||
// .get('/v1/users/invalidId')
|
||||
// .set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
// .send()
|
||||
// .expect(httpStatus.BAD_REQUEST);
|
||||
// });
|
||||
|
||||
// test('should return 400 error if userId is not a valid mongo id', async () => {
|
||||
// await insertUsers([admin]);
|
||||
// const updateBody = { name: faker.name.findName() };
|
||||
|
||||
// await request(app)
|
||||
// .patch(`/v1/users/invalidId`)
|
||||
// .set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
// .send(updateBody)
|
||||
// .expect(httpStatus.BAD_REQUEST);
|
||||
// });
|
||||
await request(app)
|
||||
.get('/v1/users/invalidId')
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send()
|
||||
.expect(httpStatus.NOT_FOUND);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('Error middlewares', () => {
|
||||
|
||||
describe('Error handler', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(logger, 'error').mockImplementation(() => {});
|
||||
jest.spyOn(logger, 'error').mockImplementation();
|
||||
});
|
||||
|
||||
test('should send proper error response and put the error message in res.locals', () => {
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
import mongoose from 'mongoose';
|
||||
import setupTestDB from '../../../utils/setupTestDB';
|
||||
import paginate from '../../../../src/models/plugins/paginate.plugin';
|
||||
import mongoose, { PaginateModel } from 'mongoose';
|
||||
import { setupTestDB } from '../../../utils/setupTestDB';
|
||||
import { paginate } from '../../../../src/models/plugins';
|
||||
|
||||
const projectSchema = new mongoose.Schema({
|
||||
interface IProject extends mongoose.Document {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface ITask extends mongoose.Document {
|
||||
name: string;
|
||||
project: mongoose.Schema.Types.ObjectId;
|
||||
}
|
||||
|
||||
const projectSchema = new mongoose.Schema<IProject>({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
@@ -16,22 +25,24 @@ projectSchema.virtual('tasks', {
|
||||
});
|
||||
|
||||
projectSchema.plugin(paginate);
|
||||
const Project = mongoose.model('Project', projectSchema);
|
||||
|
||||
const taskSchema = new mongoose.Schema({
|
||||
const Project = mongoose.model<IProject, PaginateModel<IProject>>('Project', projectSchema);
|
||||
|
||||
const taskSchema = new mongoose.Schema<ITask>({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
project: {
|
||||
type: mongoose.SchemaTypes.ObjectId,
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'Project',
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
taskSchema.plugin(paginate);
|
||||
const Task = mongoose.model('Task', taskSchema);
|
||||
|
||||
const Task = mongoose.model<ITask, PaginateModel<ITask>>('Task', taskSchema);
|
||||
|
||||
setupTestDB();
|
||||
|
||||
@@ -51,7 +62,7 @@ describe('paginate plugin', () => {
|
||||
const task = await Task.create({ name: 'Task One', project: project._id });
|
||||
|
||||
const projectPages = await Project.paginate({ _id: project._id }, { populate: 'tasks.project' });
|
||||
const { tasks } = projectPages.results[0];
|
||||
const { tasks } = projectPages.results[0] as any;
|
||||
|
||||
expect(tasks).toHaveLength(1);
|
||||
expect(tasks[0]).toHaveProperty('_id', task._id);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { generateUsername } from '../../utils/generateUsername';
|
||||
|
||||
const faker = require('faker');
|
||||
const { User } = require('../../../src/models');
|
||||
|
||||
@@ -10,6 +12,9 @@ describe('User model', () => {
|
||||
email: faker.internet.email().toLowerCase(),
|
||||
password: 'password1',
|
||||
role: 'user',
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
type: 'individual',
|
||||
username: generateUsername(),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
import mongoose from 'mongoose';
|
||||
import config from '../../src/config/config';
|
||||
import { connectGenerator, generator } from '../../src/services/data.service';
|
||||
import { connectGenerator, generator } from '../../src/services/document.service';
|
||||
|
||||
export const setupTestDB = () => {
|
||||
beforeAll(async () => {
|
||||
await mongoose.connect(config.mongoose.url, config.mongoose.options);
|
||||
await connectGenerator()
|
||||
await connectGenerator();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const db = mongoose.connection.db;
|
||||
const collections = ['IndividualData', 'OrgData', 'EntrepreneurData', 'PaymentData'];
|
||||
|
||||
await Promise.all(
|
||||
collections.map(async (name) => {
|
||||
const collection = db.collection(name);
|
||||
return collection.deleteMany({});
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(Object.values(mongoose.connection.collections).map(async (collection) => collection.deleteMany({})));
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await mongoose.disconnect();
|
||||
await generator.disconnect();
|
||||
await mongoose.disconnect();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
"lib": ["ES2015"],
|
||||
"outDir": "dist",
|
||||
"strict": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"esModuleInterop": true,
|
||||
"allowJs": false,
|
||||
"noImplicitAny": false,
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.72](https://github.com/copenomics/coopdoc-generator-ts/compare/coopdoc-generator-ts@1.0.72-alpha.3...coopdoc-generator-ts@1.0.72) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package coopdoc-generator-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.71](https://github.com/copenomics/coopdoc-generator-ts/compare/coopdoc-generator-ts@1.0.71-alpha.0...coopdoc-generator-ts@1.0.71) (2024-07-15)
|
||||
|
||||
**Note:** Version bump only for package coopdoc-generator-ts
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "coopdoc-generator-ts",
|
||||
"type": "module",
|
||||
"version": "1.0.71",
|
||||
"version": "1.0.73-alpha.0",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.0.6",
|
||||
"description": "",
|
||||
@@ -42,8 +42,7 @@
|
||||
"prepublishOnly": "nr build",
|
||||
"release": "bumpp && npm publish",
|
||||
"test": "vitest --dir test --testTimeout=120000 --exclude documents --watch=false",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"prepare": "simple-git-hooks"
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": "^8.13.0",
|
||||
@@ -74,7 +73,6 @@
|
||||
"concurrently": "^8.2.2",
|
||||
"eslint": "^8.57.0",
|
||||
"esno": "^4.7.0",
|
||||
"lint-staged": "^15.2.2",
|
||||
"pnpm": "^8.15.8",
|
||||
"rimraf": "^5.0.7",
|
||||
"simple-git-hooks": "^2.11.1",
|
||||
@@ -92,11 +90,5 @@
|
||||
"tar@<6.2.1": ">=6.2.1",
|
||||
"vite@>=5.1.0 <=5.1.6": ">=5.1.7"
|
||||
}
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "pnpm lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*": "eslint --fix"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { JSONSchemaType } from 'ajv'
|
||||
import moment from 'moment-timezone'
|
||||
import { DraftContract, SovietContract } from 'cooptypes'
|
||||
import type { ICombinedData, IGeneratedDocument, IMetaDocument, IMetaDocumentPartial, ITemplate, ITranslations } from '../Interfaces'
|
||||
import type { ICombinedData, IGeneratedDocument, IMetaDocument, IMetaDocumentPartial, ITemplate, ITranslations, externalDataTypes } from '../Interfaces'
|
||||
import type { MongoDBConnector } from '../Services/Databazor'
|
||||
import type { IndividualData, OrganizationData } from '../Models'
|
||||
import { Individual, Organization } from '../Models'
|
||||
import type { IDecisionData, IGenerate } from '../Interfaces/Documents'
|
||||
import { PDFService } from '../Services/Generator'
|
||||
@@ -11,7 +10,6 @@ import packageJson from '../../package.json'
|
||||
import { Validator } from '../Services/Validator'
|
||||
import type { CooperativeData } from '../Models/Cooperative'
|
||||
import { Cooperative } from '../Models/Cooperative'
|
||||
import type { EntrepreneurData } from '../Models/Entrepreneur'
|
||||
import { Entrepreneur } from '../Models/Entrepreneur'
|
||||
import { getFetch } from '../Utils/getFetch'
|
||||
import { getEnvVar } from '../config'
|
||||
@@ -32,16 +30,16 @@ export abstract class DocFactory {
|
||||
return new Validator(schema, combinedData).validate()
|
||||
}
|
||||
|
||||
async getUser(username: string, block_num?: number): Promise<{ type: string, data: IndividualData | OrganizationData | EntrepreneurData }> {
|
||||
async getUser(username: string, block_num?: number): Promise<{ type: string, data: externalDataTypes }> {
|
||||
const block_filter = block_num ? { block_num: { $lte: block_num } } : {}
|
||||
|
||||
const individual = await new Individual(this.storage).getOne({ username, ...block_filter })
|
||||
const organization = await new Organization(this.storage).getOne({ username, ...block_filter })
|
||||
const entrepreneur = await new Entrepreneur(this.storage).getOne({ username, ...block_filter })
|
||||
|
||||
const userData: { type: string, data: IndividualData | OrganizationData | EntrepreneurData } = {
|
||||
const userData: { type: string, data: externalDataTypes } = {
|
||||
type: '',
|
||||
data: {} as IndividualData | OrganizationData | EntrepreneurData,
|
||||
data: {} as externalDataTypes,
|
||||
}
|
||||
|
||||
if (individual) {
|
||||
@@ -80,8 +78,6 @@ export abstract class DocFactory {
|
||||
}
|
||||
|
||||
async getDecision(coop: CooperativeData, coopname: string, decision_id: number, created_at: string): Promise<IDecisionData> {
|
||||
// TODO запретить снимать голоса
|
||||
|
||||
const votes_for_actions = (await getFetch(`${getEnvVar('SIMPLE_EXPLORER_API')}/get-actions`, new URLSearchParams({
|
||||
filter: JSON.stringify({
|
||||
'account': process.env.SOVIET_CONTRACT,
|
||||
@@ -174,7 +170,7 @@ export abstract class DocFactory {
|
||||
}
|
||||
|
||||
async generatePDF(
|
||||
data: IndividualData | OrganizationData | EntrepreneurData,
|
||||
data: externalDataTypes,
|
||||
context: string,
|
||||
vars: ICombinedData,
|
||||
translation: ITranslations,
|
||||
@@ -189,7 +185,7 @@ export abstract class DocFactory {
|
||||
return document
|
||||
}
|
||||
|
||||
getFullName(data: IndividualData | OrganizationData | EntrepreneurData): string {
|
||||
getFullName(data: externalDataTypes): string {
|
||||
if ('first_name' in data)
|
||||
return `${data.last_name} ${data.first_name} ${data.middle_name}`
|
||||
|
||||
@@ -201,10 +197,6 @@ export abstract class DocFactory {
|
||||
|
||||
async saveDraft(document: IGeneratedDocument): Promise<void> {
|
||||
await this.storage.saveDraft(document)
|
||||
|
||||
// TODO вынести сохранение на диск в отдельный сервис
|
||||
// const filename = `!${document.meta.title}-${document.meta.username}-${document.meta.created_at}.pdf`
|
||||
// await saveBufferToDisk(document.binary, filename)
|
||||
}
|
||||
|
||||
async getMeta<T extends IMetaDocumentPartial>({
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import type { Cooperative } from 'cooptypes'
|
||||
|
||||
export type Currency = 'RUB' | 'Other'
|
||||
|
||||
export interface IBankAccount {
|
||||
currency: Currency
|
||||
card_number?: string // < номер карты
|
||||
bank_name: string // < наименование банка
|
||||
account_number: string // < номер счёта
|
||||
details: {
|
||||
bik: string // < бик
|
||||
corr: string // < корр счёт
|
||||
kpp: string // < кпп
|
||||
}
|
||||
}
|
||||
export type IBankAccount = Cooperative.Payments.IBankAccount
|
||||
|
||||
export type IPaymentData = Cooperative.Payments.IPaymentData
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { JSONSchemaType } from 'ajv'
|
||||
import type { Cooperative } from 'cooptypes'
|
||||
import type { IndividualData, OrganizationData } from '../Models'
|
||||
import type { DocumentsMappingByActionAndCode } from '../Templates/registry'
|
||||
import type { EntrepreneurData } from '../Models/Entrepreneur'
|
||||
import type { CooperativeData } from '../Models/Cooperative'
|
||||
import type { ExternalEntrepreneurData, ExternalIndividualData, ExternalOrganizationData } from '../Models'
|
||||
|
||||
// Определение базового интерфейса для мета-информации
|
||||
export type IMetaDocument = Cooperative.Documents.IMetaDocument
|
||||
@@ -41,9 +40,9 @@ export interface ITemplate<T> {
|
||||
}
|
||||
|
||||
export interface ICombinedData {
|
||||
individual?: IndividualData
|
||||
organization?: OrganizationData
|
||||
entrepreneur?: EntrepreneurData
|
||||
individual?: ExternalIndividualData
|
||||
organization?: ExternalOrganizationData
|
||||
entrepreneur?: ExternalEntrepreneurData
|
||||
coop: CooperativeData
|
||||
meta: IMetaDocument
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import type { Document } from 'mongodb'
|
||||
import type { ExternalEntrepreneurData, ExternalIndividualData, ExternalOrganizationData, InternalEntrepreneurData, InternalIndividualData, InternalOrganizationData } from '../Models'
|
||||
import type { PaymentData } from '../Models/PaymentMethod'
|
||||
import type { IGeneratedDocument } from './Documents'
|
||||
import type { IPaymentData } from './BankAccounts'
|
||||
|
||||
export interface IFilterDeltas {
|
||||
present: boolean
|
||||
@@ -30,3 +33,7 @@ export interface IBCState {
|
||||
|
||||
export interface IFilterDocuments extends Document, IGeneratedDocument {
|
||||
}
|
||||
|
||||
export type internalFilterTypes = InternalIndividualData | InternalEntrepreneurData | InternalOrganizationData | IPaymentData
|
||||
export type externalDataTypes = ExternalIndividualData | ExternalEntrepreneurData | ExternalOrganizationData | PaymentData
|
||||
export type externalDataTypesArrays = ExternalIndividualData[] | ExternalEntrepreneurData[] | ExternalOrganizationData[] | PaymentData[]
|
||||
|
||||
@@ -7,8 +7,10 @@ import type { MongoDBConnector } from '../Services/Databazor'
|
||||
import { individualSchema } from '../Schema/IndividualSchema'
|
||||
import { getFetch } from '../Utils/getFetch'
|
||||
import { getEnvVar } from '../config'
|
||||
import { Organization, organizationSchema } from './Organization'
|
||||
import type { IndividualData } from './Individual'
|
||||
import { organizationSchema } from '../Schema'
|
||||
import { Organization } from './Organization'
|
||||
|
||||
import type { ExternalIndividualData } from './Individual'
|
||||
import { Individual } from './Individual'
|
||||
|
||||
export type CooperativeData = CooperativeApi.Model.ICooperativeData
|
||||
@@ -21,6 +23,9 @@ export const CooperativeSchema: JSONSchemaType<CooperativeData> = {
|
||||
registration: { type: 'string' },
|
||||
initial: { type: 'string' },
|
||||
minimum: { type: 'string' },
|
||||
org_registration: { type: 'string' },
|
||||
org_initial: { type: 'string' },
|
||||
org_minimum: { type: 'string' },
|
||||
totalMembers: { type: 'number' },
|
||||
members: {
|
||||
type: 'array',
|
||||
@@ -28,26 +33,26 @@ export const CooperativeSchema: JSONSchemaType<CooperativeData> = {
|
||||
},
|
||||
...organizationSchema.properties,
|
||||
},
|
||||
required: [...organizationSchema.required, 'is_branched', 'registration', 'initial', 'minimum', 'members', 'totalMembers'],
|
||||
required: [...organizationSchema.required, 'is_branched', 'registration', 'initial', 'minimum', 'org_registration', 'org_initial', 'org_minimum', 'members', 'totalMembers'],
|
||||
additionalProperties: true,
|
||||
}
|
||||
|
||||
export class Cooperative {
|
||||
cooperative: CooperativeData | null
|
||||
storage: MongoDBConnector
|
||||
db: MongoDBConnector
|
||||
|
||||
private data_service: DataService<CooperativeData>
|
||||
|
||||
constructor(storage: MongoDBConnector) {
|
||||
this.storage = storage
|
||||
this.db = storage
|
||||
this.cooperative = null
|
||||
this.data_service = new DataService<CooperativeData>(storage, 'IndividualData')
|
||||
this.data_service = new DataService<CooperativeData>(storage, 'CooperativeData')
|
||||
}
|
||||
|
||||
async getOne(username: string, block_num?: number): Promise<CooperativeData> {
|
||||
const block_filter = block_num ? { block_num: { $lte: block_num } } : {}
|
||||
|
||||
const organizationPrivateData = await new Organization(this.storage).getOne({ username, ...block_filter })
|
||||
const organizationPrivateData = await new Organization(this.db).getOne({ username, ...block_filter })
|
||||
|
||||
if (!organizationPrivateData)
|
||||
throw new Error('Информация о организации не обнаружена в базе данных.')
|
||||
@@ -77,15 +82,15 @@ export class Cooperative {
|
||||
const soviet = soviet_response.results[0]?.value as SovietContract.Tables.Boards.IBoards
|
||||
|
||||
if (!soviet)
|
||||
throw new Error('Совет кооператива не обранужен в базе данных.')
|
||||
throw new Error('Совет кооператива не обнаружен в базе данных.')
|
||||
|
||||
const userModel = new Individual(this.storage)
|
||||
const userModel = new Individual(this.db)
|
||||
|
||||
const members = [] as MembersData[]
|
||||
let chairman = {} as IndividualData
|
||||
let chairman = {} as ExternalIndividualData
|
||||
|
||||
for (const member of soviet.members) {
|
||||
const userData = await userModel.getOne({ username: member.username, ...block_filter }) as IndividualData
|
||||
const userData = await userModel.getOne({ username: member.username, ...block_filter }) as ExternalIndividualData
|
||||
if (!userData)
|
||||
throw new Error(`Пользователь ${member.username} не найден в базе данных.`)
|
||||
|
||||
|
||||
@@ -1,40 +1,111 @@
|
||||
import type { Filter, InsertOneResult } from 'mongodb'
|
||||
import type { Filter, InsertOneResult, UpdateResult } from 'mongodb'
|
||||
import type { Cooperative } from 'cooptypes'
|
||||
import type { ValidateResult } from '../Services/Validator'
|
||||
import { Validator } from '../Services/Validator'
|
||||
import type { MongoDBConnector } from '../Services/Databazor'
|
||||
import DataService from '../Services/Databazor/DataService'
|
||||
import { entrepreneurSchema } from '../Schema/EntrepreneurSchema'
|
||||
import type { IBankAccount } from '../Interfaces'
|
||||
import { getCurrentBlock } from '../Utils/getCurrentBlock'
|
||||
import type { PaymentData } from './PaymentMethod'
|
||||
import { PaymentMethod } from './PaymentMethod'
|
||||
|
||||
export type EntrepreneurData = Cooperative.Users.IEntrepreneurData
|
||||
export type ExternalEntrepreneurData = Cooperative.Users.IEntrepreneurData
|
||||
|
||||
export type InternalEntrepreneurData = Omit<ExternalEntrepreneurData, 'bank_account'> & {
|
||||
bank_account?: Cooperative.Payments.IBankAccount
|
||||
}
|
||||
|
||||
export class Entrepreneur {
|
||||
entrepreneur?: EntrepreneurData
|
||||
private data_service: DataService<EntrepreneurData>
|
||||
db: MongoDBConnector
|
||||
entrepreneur?: ExternalEntrepreneurData
|
||||
private data_service: DataService<InternalEntrepreneurData>
|
||||
|
||||
constructor(storage: MongoDBConnector, data?: EntrepreneurData) {
|
||||
constructor(storage: MongoDBConnector, data?: ExternalEntrepreneurData) {
|
||||
this.db = storage
|
||||
this.entrepreneur = data
|
||||
this.data_service = new DataService<EntrepreneurData>(storage, 'EntrepreneurData')
|
||||
this.data_service = new DataService<InternalEntrepreneurData>(storage, 'EntrepreneurData')
|
||||
}
|
||||
|
||||
validate(): ValidateResult {
|
||||
return new Validator(entrepreneurSchema, this.entrepreneur as EntrepreneurData).validate()
|
||||
return new Validator(entrepreneurSchema, this.entrepreneur).validate()
|
||||
}
|
||||
|
||||
async save(): Promise<InsertOneResult<EntrepreneurData>> {
|
||||
async save(): Promise<InsertOneResult<InternalEntrepreneurData>> {
|
||||
await this.validate()
|
||||
return await this.data_service.save(this.entrepreneur as EntrepreneurData)
|
||||
|
||||
if (!this.entrepreneur)
|
||||
throw new Error('Данные ИП не предоставлены для сохранения')
|
||||
|
||||
const { bank_account, ...entr_for_save } = this.entrepreneur
|
||||
|
||||
const currentBlock = await getCurrentBlock()
|
||||
|
||||
const bankData: PaymentData = {
|
||||
username: this.entrepreneur.username,
|
||||
method_id: 1,
|
||||
user_type: 'entrepreneur',
|
||||
method_type: 'bank_transfer',
|
||||
is_default: true,
|
||||
data: bank_account as Cooperative.Payments.IBankAccount,
|
||||
deleted: false,
|
||||
block_num: currentBlock,
|
||||
}
|
||||
|
||||
const paymentMethod = await new PaymentMethod(this.db, bankData)
|
||||
await paymentMethod.validate()
|
||||
await paymentMethod.save()
|
||||
|
||||
const entrForSave: InternalEntrepreneurData = {
|
||||
...entr_for_save,
|
||||
deleted: false,
|
||||
block_num: currentBlock,
|
||||
}
|
||||
|
||||
return await this.data_service.save(entrForSave)
|
||||
}
|
||||
|
||||
async getOne(filter: Filter<EntrepreneurData>): Promise<EntrepreneurData | null> {
|
||||
return this.data_service.getOne(filter)
|
||||
async getOne(filter: Filter<InternalEntrepreneurData>): Promise<ExternalEntrepreneurData | null> {
|
||||
const entr = await this.data_service.getOne(filter)
|
||||
const blockFilter = filter.block_num ? { block_num: filter.block_num } : {}
|
||||
|
||||
if (entr)
|
||||
entr.bank_account = (await (new PaymentMethod(this.db).getOne({ ...blockFilter, username: entr.username, method_type: 'bank_transfer', is_default: true })))?.data as IBankAccount
|
||||
return entr as ExternalEntrepreneurData
|
||||
}
|
||||
|
||||
async getMany(filter: Filter<EntrepreneurData>): Promise<EntrepreneurData[]> {
|
||||
return this.data_service.getMany(filter)
|
||||
async getMany(filter: Filter<InternalEntrepreneurData>): Promise<Cooperative.Documents.IGetResponse<ExternalEntrepreneurData>> {
|
||||
const response = await this.data_service.getMany({ deleted: false, ...filter }, 'username')
|
||||
const entrepreneurs = response.results
|
||||
const blockFilter = filter.block_num ? { block_num: filter.block_num } : {}
|
||||
|
||||
for (const entr of entrepreneurs) {
|
||||
entr.bank_account = (await (new PaymentMethod(this.db).getOne({ ...blockFilter, username: entr.username, method_type: 'bank_transfer', is_default: true })))?.data as IBankAccount
|
||||
}
|
||||
|
||||
const result: Cooperative.Documents.IGetResponse<ExternalEntrepreneurData> = {
|
||||
results: entrepreneurs as ExternalEntrepreneurData[],
|
||||
page: response.page,
|
||||
limit: response.limit,
|
||||
totalResults: response.totalResults,
|
||||
totalPages: response.totalPages,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async getHistory(filter: Filter<EntrepreneurData>): Promise<EntrepreneurData[]> {
|
||||
return this.data_service.getHistory(filter)
|
||||
async getHistory(filter: Filter<InternalEntrepreneurData>): Promise<ExternalEntrepreneurData[]> {
|
||||
const history = await this.data_service.getHistory(filter)
|
||||
const blockFilter = filter.block_num ? { block_num: filter.block_num } : {}
|
||||
|
||||
for (const entr of history) {
|
||||
entr.bank_account = (await (new PaymentMethod(this.db).getOne({ ...blockFilter, username: entr.username, method_type: 'bank_transfer', is_default: true })))?.data as IBankAccount
|
||||
}
|
||||
|
||||
return history as ExternalEntrepreneurData[]
|
||||
}
|
||||
|
||||
async del(filter: Filter<InternalEntrepreneurData>): Promise<UpdateResult> {
|
||||
return this.data_service.updateMany({ ...filter, deleted: false }, { deleted: true })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,58 @@
|
||||
import type { Filter, InsertOneResult } from 'mongodb'
|
||||
import type { Filter, InsertOneResult, UpdateResult } from 'mongodb'
|
||||
import type { Cooperative } from 'cooptypes'
|
||||
import type { ValidateResult } from '../Services/Validator'
|
||||
import { Validator } from '../Services/Validator'
|
||||
import type { MongoDBConnector } from '../Services/Databazor'
|
||||
import DataService from '../Services/Databazor/DataService'
|
||||
import { individualSchema } from '../Schema/IndividualSchema'
|
||||
import { getCurrentBlock } from '../Utils/getCurrentBlock'
|
||||
|
||||
export type IndividualData = Cooperative.Users.IIndividualData
|
||||
export type ExternalIndividualData = Cooperative.Users.IIndividualData
|
||||
export type InternalIndividualData = ExternalIndividualData
|
||||
|
||||
export class Individual {
|
||||
individual?: IndividualData
|
||||
private data_service: DataService<IndividualData>
|
||||
individual?: ExternalIndividualData
|
||||
private data_service: DataService<InternalIndividualData>
|
||||
|
||||
constructor(storage: MongoDBConnector, data?: IndividualData) {
|
||||
constructor(storage: MongoDBConnector, data?: ExternalIndividualData) {
|
||||
this.individual = data
|
||||
this.data_service = new DataService<IndividualData>(storage, 'IndividualData')
|
||||
this.data_service = new DataService<InternalIndividualData>(storage, 'IndividualData')
|
||||
}
|
||||
|
||||
validate(): ValidateResult {
|
||||
return new Validator(individualSchema, this.individual as IndividualData).validate()
|
||||
return new Validator(individualSchema, this.individual).validate()
|
||||
}
|
||||
|
||||
async save(): Promise<InsertOneResult<IndividualData>> {
|
||||
async save(): Promise<InsertOneResult<InternalIndividualData>> {
|
||||
await this.validate()
|
||||
return await this.data_service.save(this.individual as IndividualData)
|
||||
|
||||
if (!this.individual)
|
||||
throw new Error('Данные физлица не предоставлены для сохранения')
|
||||
|
||||
const currentBlock = await getCurrentBlock()
|
||||
|
||||
const individual: InternalIndividualData = {
|
||||
...this.individual,
|
||||
deleted: false,
|
||||
block_num: currentBlock,
|
||||
}
|
||||
|
||||
return await this.data_service.save(individual)
|
||||
}
|
||||
|
||||
async getOne(filter: Filter<IndividualData>): Promise<IndividualData | null> {
|
||||
async getOne(filter: Filter<InternalIndividualData>): Promise<ExternalIndividualData | null> {
|
||||
return this.data_service.getOne(filter)
|
||||
}
|
||||
|
||||
async getMany(filter: Filter<IndividualData>): Promise<IndividualData[]> {
|
||||
return this.data_service.getMany(filter)
|
||||
async getMany(filter: Filter<InternalIndividualData>): Promise<Cooperative.Documents.IGetResponse<ExternalIndividualData>> {
|
||||
return this.data_service.getMany(filter, 'username')
|
||||
}
|
||||
|
||||
async getHistory(filter: Filter<IndividualData>): Promise<IndividualData[]> {
|
||||
async getHistory(filter: Filter<InternalIndividualData>): Promise<ExternalIndividualData[]> {
|
||||
return this.data_service.getHistory(filter)
|
||||
}
|
||||
|
||||
async del(filter: Filter<InternalIndividualData>): Promise<UpdateResult> {
|
||||
return this.data_service.updateMany(filter, { deleted: true })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +1,111 @@
|
||||
import type { JSONSchemaType } from 'ajv'
|
||||
import type { Filter, InsertOneResult } from 'mongodb'
|
||||
import type { Filter, InsertOneResult, UpdateResult } from 'mongodb'
|
||||
import type { Cooperative } from 'cooptypes'
|
||||
import type { ValidateResult } from '../Services/Validator'
|
||||
import { Validator } from '../Services/Validator'
|
||||
import DataService from '../Services/Databazor/DataService'
|
||||
import type { MongoDBConnector } from '../Services/Databazor'
|
||||
import { BankAccountSchema } from '../Schema/BankAccount'
|
||||
import { organizationSchema } from '../Schema'
|
||||
import type { IBankAccount } from '../Interfaces'
|
||||
import { getCurrentBlock } from '../Utils/getCurrentBlock'
|
||||
import type { PaymentData } from './PaymentMethod'
|
||||
import { PaymentMethod } from './PaymentMethod'
|
||||
|
||||
export type OrganizationData = Cooperative.Users.IOrganizationData
|
||||
export type ExternalOrganizationData = Cooperative.Users.IOrganizationData
|
||||
|
||||
export const organizationSchema: JSONSchemaType<OrganizationData> = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
username: { type: 'string' },
|
||||
type: { type: 'string', enum: ['coop', 'ooo', 'oao', 'zao', 'pao', 'ao'] },
|
||||
is_cooperative: { type: 'boolean' },
|
||||
short_name: { type: 'string' },
|
||||
full_name: { type: 'string' },
|
||||
represented_by: {
|
||||
type: 'object',
|
||||
required: ['first_name', 'last_name', 'position', 'based_on'],
|
||||
properties: {
|
||||
first_name: { type: 'string' },
|
||||
last_name: { type: 'string' },
|
||||
middle_name: { type: 'string' },
|
||||
position: { type: 'string' },
|
||||
based_on: { type: 'string' },
|
||||
// signature: { type: 'string' },
|
||||
},
|
||||
additionalProperties: true,
|
||||
},
|
||||
country: { type: 'string', enum: ['Russia', 'Other'] },
|
||||
city: { type: 'string' },
|
||||
email: { type: 'string' },
|
||||
phone: { type: 'string' },
|
||||
full_address: { type: 'string' },
|
||||
details: {
|
||||
type: 'object',
|
||||
required: ['inn', 'ogrn'],
|
||||
properties: {
|
||||
inn: { type: 'string' },
|
||||
ogrn: { type: 'string' },
|
||||
},
|
||||
additionalProperties: true,
|
||||
},
|
||||
bank_account: {
|
||||
type: 'object',
|
||||
required: BankAccountSchema.required,
|
||||
properties: BankAccountSchema.properties,
|
||||
},
|
||||
},
|
||||
required: ['username', 'type', 'is_cooperative', 'short_name', 'full_name', 'represented_by', 'country', 'city', 'full_address', 'email', 'phone', 'details', 'bank_account'],
|
||||
additionalProperties: true,
|
||||
export type InternalOrganizationData = Omit<ExternalOrganizationData, 'bank_account'> & {
|
||||
bank_account?: Cooperative.Payments.IBankAccount
|
||||
}
|
||||
|
||||
export class Organization {
|
||||
organization?: OrganizationData
|
||||
private data_service: DataService<OrganizationData>
|
||||
db: MongoDBConnector
|
||||
organization?: ExternalOrganizationData
|
||||
private data_service: DataService<InternalOrganizationData>
|
||||
|
||||
constructor(storage: MongoDBConnector, data?: OrganizationData) {
|
||||
constructor(storage: MongoDBConnector, data?: ExternalOrganizationData) {
|
||||
this.db = storage
|
||||
this.organization = data
|
||||
this.data_service = new DataService<OrganizationData>(storage, 'OrgData')
|
||||
this.data_service = new DataService<InternalOrganizationData>(storage, 'OrgData')
|
||||
}
|
||||
|
||||
validate(): ValidateResult {
|
||||
return new Validator(organizationSchema, this.organization as OrganizationData).validate()
|
||||
return new Validator(organizationSchema, this.organization as ExternalOrganizationData).validate()
|
||||
}
|
||||
|
||||
async save(): Promise<InsertOneResult<OrganizationData>> {
|
||||
async save(): Promise<InsertOneResult<InternalOrganizationData>> {
|
||||
await this.validate()
|
||||
|
||||
return await this.data_service.save(this.organization as OrganizationData)
|
||||
if (!this.organization)
|
||||
throw new Error('Данные организации не предоставлены для сохранения')
|
||||
|
||||
const { bank_account, ...organization_for_save } = this.organization
|
||||
const currentBlock = await getCurrentBlock()
|
||||
|
||||
const bankData: PaymentData = {
|
||||
username: this.organization.username,
|
||||
method_id: 1,
|
||||
user_type: 'organization',
|
||||
method_type: 'bank_transfer',
|
||||
is_default: true,
|
||||
data: bank_account as Cooperative.Payments.IBankAccount,
|
||||
deleted: false,
|
||||
block_num: currentBlock,
|
||||
}
|
||||
|
||||
const paymentMethod = await new PaymentMethod(this.db, bankData)
|
||||
await paymentMethod.validate()
|
||||
await paymentMethod.save()
|
||||
|
||||
const orgForSave: InternalOrganizationData = {
|
||||
...organization_for_save,
|
||||
deleted: false,
|
||||
block_num: currentBlock,
|
||||
}
|
||||
|
||||
return await this.data_service.save(orgForSave)
|
||||
}
|
||||
|
||||
async getOne(filter: Filter<OrganizationData>): Promise<OrganizationData | null> {
|
||||
return this.data_service.getOne(filter)
|
||||
async getOne(filter: Filter<InternalOrganizationData>): Promise<ExternalOrganizationData | null> {
|
||||
const org = await this.data_service.getOne(filter)
|
||||
const blockFilter = filter.block_num ? { block_num: filter.block_num } : {}
|
||||
|
||||
if (org) {
|
||||
org.bank_account = (await (new PaymentMethod(this.db).getOne({ ...blockFilter, username: org.username, method_type: 'bank_transfer', is_default: true })))?.data as IBankAccount
|
||||
}
|
||||
|
||||
return org as ExternalOrganizationData
|
||||
}
|
||||
|
||||
async getMany(filter: Filter<OrganizationData>): Promise<OrganizationData[]> {
|
||||
return this.data_service.getMany(filter)
|
||||
async getMany(filter: Filter<InternalOrganizationData>): Promise<Cooperative.Documents.IGetResponse<ExternalOrganizationData>> {
|
||||
const response = (await this.data_service.getMany({ deleted: false, ...filter }, 'username'))
|
||||
const orgs = response.results
|
||||
const blockFilter = filter.block_num ? { block_num: filter.block_num } : {}
|
||||
|
||||
for (const org of orgs) {
|
||||
org.bank_account = (await (new PaymentMethod(this.db).getOne({ ...blockFilter, username: org.username, method_type: 'bank_transfer', is_default: true })))?.data as IBankAccount
|
||||
}
|
||||
|
||||
const result: Cooperative.Documents.IGetResponse<ExternalOrganizationData> = {
|
||||
results: orgs as ExternalOrganizationData[],
|
||||
page: response.page,
|
||||
limit: response.limit,
|
||||
totalResults: response.totalResults,
|
||||
totalPages: response.totalPages,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async getHistory(filter: Filter<OrganizationData>): Promise<OrganizationData[]> {
|
||||
return this.data_service.getHistory(filter)
|
||||
async getHistory(filter: Filter<InternalOrganizationData>): Promise<ExternalOrganizationData[]> {
|
||||
const orgs = await this.data_service.getHistory(filter)
|
||||
const blockFilter = filter.block_num ? { block_num: filter.block_num } : {}
|
||||
|
||||
for (const org of orgs) {
|
||||
org.bank_account = (await (new PaymentMethod(this.db).getOne({ ...blockFilter, username: org.username, method_type: 'bank_transfer', is_default: true })))?.data as IBankAccount
|
||||
}
|
||||
return orgs as ExternalOrganizationData[]
|
||||
}
|
||||
|
||||
async del(filter: Filter<InternalOrganizationData>): Promise<UpdateResult> {
|
||||
return this.data_service.updateMany({ ...filter }, { deleted: true })
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user