Compare commits
250 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 433a7d3641 | |||
| 6b41c74b43 | |||
| cc3e7ff7c6 | |||
| a35a6bf642 | |||
| 9ecd523646 | |||
| e7f896c38e | |||
| 624ec743b1 | |||
| 553c5745ef | |||
| 3f2779ca3d | |||
| e51b962627 | |||
| f709e135b6 | |||
| 50b48aa247 | |||
| 508f2abc82 | |||
| 42ec56e578 | |||
| 92673dac6e | |||
| daf57aba35 | |||
| dee629c429 | |||
| 67f9092350 | |||
| 88691d47a5 | |||
| 0fdd9bc803 | |||
| 8452c607b4 | |||
| 4fc57c2d57 | |||
| 9f0b707a30 | |||
| 511bc351d5 | |||
| 56ee3e962c | |||
| 2781e8519d | |||
| 46575348fe | |||
| bae2ca646b | |||
| 0686c6ab00 | |||
| db04b66ce5 | |||
| c43c8a29af | |||
| aaea18abe1 | |||
| 0b6076cb61 | |||
| 3b6d02f02c | |||
| 625a0586a3 | |||
| 10d63ca586 | |||
| d2697bf9de | |||
| fa8c8843e2 | |||
| 7b59ea4b96 | |||
| 058215934e | |||
| 1d724d841f | |||
| 32db2100de | |||
| 095c77b65b | |||
| c1c07bff8f | |||
| 1206bd0e98 | |||
| 67bcd1b307 | |||
| 31ef70e433 | |||
| afb12961d2 | |||
| 3dbd40e334 | |||
| bc5d6e38c8 | |||
| 5a39143051 | |||
| 18e9571877 | |||
| a828f4c831 | |||
| 54ac12f250 | |||
| 9a6a72f605 | |||
| 03949489e8 | |||
| 8ebd735e7e | |||
| 39246da73f | |||
| bf33f60368 | |||
| 492a613769 | |||
| 2c02c3434e | |||
| a186ddd9d2 | |||
| 4b0c7a5e4e | |||
| 54cfbe3ab6 | |||
| 02015cf0b8 | |||
| 416e4d3598 | |||
| e0d9c4b0ea | |||
| 2659e3bc09 | |||
| d296167b6a | |||
| 89b0aa7fec | |||
| 787aadf444 | |||
| d67161a29b | |||
| 7f4777ead4 | |||
| 1e2147b143 | |||
| 9f0a2fbf63 | |||
| 7804851bc9 | |||
| adc50dd89e | |||
| 5378cf5ddc | |||
| a733bb605f | |||
| aaa1b5a264 | |||
| 5855cdee65 | |||
| c0d584e644 | |||
| 955f0f473e | |||
| 9ceb888124 | |||
| e902933693 | |||
| e07b47fb10 | |||
| 5bf08d9701 | |||
| 597fa1e6fa | |||
| dda3ec0ba0 | |||
| 991f08c749 | |||
| 923865cd7c | |||
| 606aee8ccd | |||
| 4a1cb27d0a | |||
| 26cd7f3a12 | |||
| f4045dbc56 | |||
| 33c2b24550 | |||
| cd86f3c8ea | |||
| 58026ebc2d | |||
| 9183275dad | |||
| 5e3a625d95 | |||
| 3297f9d2d2 | |||
| 29b8306a28 | |||
| d84746bac6 | |||
| 4eacf2ff0b | |||
| a68e0d6f61 | |||
| ddf44304c7 | |||
| 789d98f58e | |||
| 205574620d | |||
| d9bae12b31 | |||
| c951190794 | |||
| d4e1d69c3c | |||
| f93a32c07b | |||
| 8e7c152c0d | |||
| 6a64404f20 | |||
| 1a09589add | |||
| d0f9072ab5 | |||
| 011ed9f2d8 | |||
| 12332f83d9 | |||
| 8f75829948 | |||
| a7ffedb3b2 | |||
| 378d3b9747 | |||
| 5ba05409c4 | |||
| d86beef58f | |||
| 7315d67c9f | |||
| 59d79b8c7a | |||
| 3df97653a2 | |||
| 948d55e418 | |||
| 1d2c7281b1 | |||
| 35759b943c | |||
| 29da33e200 | |||
| 1d758f8126 | |||
| b663dac2ab | |||
| 485a3971e9 | |||
| 36b5d64ca6 | |||
| 8462a709bc | |||
| 20cabe8362 | |||
| 6967864361 | |||
| a6ee3840ae | |||
| 01de6139df | |||
| 6ba0924f6c | |||
| 00c75d1fd8 | |||
| 93f28fa9d5 | |||
| 0aed10eb8b | |||
| d84d687eee | |||
| d046f56832 | |||
| 0af1fbf313 | |||
| e9aaf4309e | |||
| 80fa1085d2 | |||
| 88195291fd | |||
| 39ad600d00 | |||
| c63a3aba6b | |||
| 782ecbe307 | |||
| f75bbfa048 | |||
| 3a772cc983 | |||
| 807113523e | |||
| 7df2af8611 | |||
| 10c1697b00 | |||
| 742e5d38ab | |||
| 1d3e7f8af0 | |||
| c99bacab64 | |||
| 95d7555ebc | |||
| 5854e0a23c | |||
| 81f7f32767 | |||
| c0374a7da1 | |||
| ed65e655b9 | |||
| d250e516b5 | |||
| e4f404b2c7 | |||
| 86571331f8 | |||
| 163d420af9 | |||
| 4925ea4af0 | |||
| 57b05ce348 | |||
| 427b53fcbd | |||
| 2ac7a31be6 | |||
| 4baed6409f | |||
| 2ea0ff0965 | |||
| 88b9a7edce | |||
| 538066b960 | |||
| b10dd1d75a | |||
| f743580b55 | |||
| 0688467302 | |||
| 29e972dbf2 | |||
| bd99697c5b | |||
| 1d5c431ff6 | |||
| 3736c510ff | |||
| 26c12c3a85 | |||
| 10bb08e5e6 | |||
| b50d5b6121 | |||
| 38b28752e1 | |||
| 87c51009de | |||
| a950a47b28 | |||
| 2609d10c0b | |||
| 7b4cb0f66c | |||
| 26db0e1e69 | |||
| 62e64cbd22 | |||
| 844a47830f | |||
| 2cc1e32f45 | |||
| 9890958b1e | |||
| 6dbd6d6edc | |||
| bf7a4dfd28 | |||
| f52626bda0 | |||
| 88cec4bf2b | |||
| b880def108 | |||
| bfd5dc039a | |||
| c0d236dff3 | |||
| 743d5fc306 | |||
| 2f51516163 | |||
| b606b46aa4 | |||
| 5b65ff99a0 | |||
| 74a3f52324 | |||
| 7c2a4fdfa8 | |||
| fecee3a098 | |||
| 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 |
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
dist
|
||||
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"editor.bracketPairColorization.enabled": true,
|
||||
"editor.guides.bracketPairs": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabCompletion": "onlySnippets",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
|
||||
"eslint.validate": ["javascript", "typescript", "vue"],
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"i18n-ally.localesPaths": ["src/i18n"],
|
||||
"typescript.format.enable": true,
|
||||
"typescript.format.indentSwitchCase": false,
|
||||
"typescript.validate.enable": true,
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.1.3](https://github.com/coopenomics/monocoop/compare/v2.1.2-alpha.10...v2.1.3) (2024-10-26)
|
||||
|
||||
|
||||
|
||||
## [2.1.2](https://github.com/coopenomics/monocoop/compare/v2.1.1...v2.1.2) (2024-10-19)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.2](https://github.com/coopenomics/monocoop/compare/v2.1.1...v2.1.2) (2024-10-19)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.1.0](https://github.com/coopenomics/monocoop/compare/v2.0.10-alpha.3...v2.1.0) (2024-10-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* запрос соглашений ([9a6a72f](https://github.com/coopenomics/monocoop/commit/9a6a72f605ba52eef2ed6f18ccee6fbed287ea00))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.9](https://github.com/coopenomics/monocoop/compare/v2.0.8...v2.0.9) (2024-10-10)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.8](https://github.com/coopenomics/monocoop/compare/v2.0.7...v2.0.8) (2024-10-09)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.7](https://github.com/coopenomics/monocoop/compare/v2.0.6...v2.0.7) (2024-09-30)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.6](https://github.com/coopenomics/monocoop/compare/v2.0.5...v2.0.6) (2024-09-30)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.5](https://github.com/coopenomics/monocoop/compare/v2.0.5-alpha.0...v2.0.5) (2024-09-30)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.2](https://github.com/coopenomics/monocoop/compare/v2.0.2-alpha.1...v2.0.2) (2024-09-29)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 2.1.0 (2024-09-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **importers:** исправлена ошибка в импорте модуля @wharfkit/contract ([bf89032](https://github.com/coopenomics/monocoop/commit/bf89032d8f66444804a2521f4eb96ffc75b0f605))
|
||||
* **terminal:** исправлен шрифт в README ([889447a](https://github.com/coopenomics/monocoop/commit/889447a93ceadb577613ddb5b1cb2ef1cc3d54b6))
|
||||
* **terminal:** исправлено описание проекта ([989b318](https://github.com/coopenomics/monocoop/commit/989b3180ded99a871e018cf26e6c493449223c01))
|
||||
* **terminal:** fix typo in README.md ([fdf9996](https://github.com/coopenomics/monocoop/commit/fdf999619d2d69e960b062fe6815f5b057c95f48))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* добавлен docker-compose.yaml ([6a46697](https://github.com/coopenomics/monocoop/commit/6a46697c9d6cc3cde14dbce8f70997c00f9850de))
|
||||
* **package:** добавлен скрипт gpt-commit для удобного коммита ([e0b5107](https://github.com/coopenomics/monocoop/commit/e0b510799bb0ac68890d572deb652beefd0651c4))
|
||||
* **terminal:** добавлена поддержка новых команд ([73e4ca2](https://github.com/coopenomics/monocoop/commit/73e4ca226acebcbea3ae59a62def99d86efb1353))
|
||||
@@ -0,0 +1,39 @@
|
||||
k# Conventional Commits
|
||||
|
||||
### Основные типы коммитов:
|
||||
|
||||
- **`feat`**: Добавление новой функциональности (feature).
|
||||
- Пример: `feat: добавлена возможность загрузки файлов`
|
||||
|
||||
- **`fix`**: Исправление бага.
|
||||
- Пример: `fix: исправлена ошибка отображения кнопки на мобильных устройствах`
|
||||
|
||||
- **`chore`**: Изменения, не влияющие на исходный код (например, обновление зависимостей или инструментария).
|
||||
- Пример: `chore: обновление зависимостей`
|
||||
|
||||
- **`docs`**: Изменения в документации.
|
||||
- Пример: `docs: обновлено руководство пользователя`
|
||||
|
||||
- **`style`**: Изменения стиля кода, не влияющие на его функциональность (форматирование, пробелы, и т.д.).
|
||||
- Пример: `style: исправлены отступы в коде`
|
||||
|
||||
- **`refactor`**: Изменения в коде, которые не исправляют баги и не добавляют новую функциональность.
|
||||
- Пример: `refactor: улучшена структура класса`
|
||||
|
||||
- **`perf`**: Изменения, направленные на улучшение производительности.
|
||||
- Пример: `perf: оптимизирована работа с массивами`
|
||||
|
||||
- **`test`**: Добавление или изменение тестов.
|
||||
- Пример: `test: добавлен тест для проверки функции отправки сообщений`
|
||||
|
||||
### Дополнительные типы коммитов:
|
||||
|
||||
- **`build`**: Изменения, связанные с процессом сборки или зависимостями.
|
||||
- Пример: `build: обновление конфигурации Webpack`
|
||||
|
||||
- **`ci`**: Изменения, касающиеся настроек непрерывной интеграции (например, Travis, Jenkins).
|
||||
- Пример: `ci: настройка Travis для автоматических сборок`
|
||||
|
||||
- **`revert`**: Откат на предыдущие изменения.
|
||||
- Пример: `revert: откат коммита e0b5107`
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm install -g pnpm lerna
|
||||
RUN pnpm install
|
||||
RUN lerna run build
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
# MONOCOOP
|
||||
|
||||
Моно-репозиторий компонент Цифрового Кооператива.
|
||||
Моно-репозиторий компонент клиента системы электронного документооборота для потребительских и производственных кооперативов.
|
||||
|
||||
## Инициализация
|
||||
```
|
||||
curl -X POST http://127.0.0.1:2998/v1/system/init \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "server-secret: SECRET" \
|
||||
-d @init-cooperative1.json
|
||||
```
|
||||
|
||||
## Лицензия
|
||||
Продукт Потребительского Кооператива "ВОСХОД" распространяется по лицензии BY-NC-SA 4.0.
|
||||
|
||||
Разрешено делиться, копировать и распространять материал на любом носителе и форме, адаптировать, делать ремиксы, видоизменять и создавать новое, опираясь на этот материал. При использовании, Вы должны обеспечить указание авторства, предоставить ссылку, и обозначить изменения, если таковые были сделаны. Если вы перерабатываете, преобразовываете материал или берёте его за основу для производного произведения, вы должны распространять переделанные вами части материала на условиях той же лицензии , в соответствии с которой распространяется оригинал. Запрещено коммерческое использование материала. Использование в коммерческих целях – это использование, в первую очередь направленное на получение коммерческого преимущества или денежного вознаграждения.
|
||||
|
||||
Юридический текст лицензии: https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.ru
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -3,6 +3,82 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.1.3](https://github.com/copenomics/cooparser/compare/v2.1.2-alpha.10...v2.1.3) (2024-10-26)
|
||||
|
||||
|
||||
|
||||
## [2.1.2](https://github.com/copenomics/cooparser/compare/v2.1.1...v2.1.2) (2024-10-19)
|
||||
|
||||
**Note:** Version bump only for package cooparser-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.2](https://github.com/copenomics/cooparser/compare/v2.1.1...v2.1.2) (2024-10-19)
|
||||
|
||||
**Note:** Version bump only for package cooparser-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.1.0](https://github.com/copenomics/cooparser/compare/v2.0.10-alpha.3...v2.1.0) (2024-10-13)
|
||||
|
||||
**Note:** Version bump only for package cooparser-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.9](https://github.com/copenomics/cooparser/compare/v2.0.8...v2.0.9) (2024-10-10)
|
||||
|
||||
**Note:** Version bump only for package cooparser-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.8](https://github.com/copenomics/cooparser/compare/v2.0.7...v2.0.8) (2024-10-09)
|
||||
|
||||
**Note:** Version bump only for package cooparser-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.2](https://github.com/copenomics/cooparser/compare/v2.0.2-alpha.1...v2.0.2) (2024-09-29)
|
||||
|
||||
**Note:** Version bump only for package cooparser-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.20](https://github.com/copenomics/cooparser/compare/cooparser-ts@1.0.20-alpha.0...cooparser-ts@1.0.20) (2024-09-29)
|
||||
|
||||
**Note:** Version bump only for package cooparser-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.18](https://github.com/copenomics/cooparser/compare/cooparser-ts@1.0.17...cooparser-ts@1.0.18) (2024-08-25)
|
||||
|
||||
**Note:** Version bump only for package cooparser-ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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": "2.1.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pkg-placeholder",
|
||||
"version": "1.0.16",
|
||||
"version": "2.1.3",
|
||||
"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,8 +1,8 @@
|
||||
{
|
||||
"name": "cooparser-ts",
|
||||
"type": "module",
|
||||
"version": "1.0.16",
|
||||
"private": true,
|
||||
"version": "2.1.3",
|
||||
"private": false,
|
||||
"packageManager": "pnpm@9.0.6",
|
||||
"description": "",
|
||||
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
|
||||
@@ -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",
|
||||
@@ -83,10 +83,5 @@
|
||||
"vite": "^5.2.10",
|
||||
"vitest": "^1.5.2"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "pnpm lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*": "eslint --fix"
|
||||
}
|
||||
"gitHead": "7b59ea4b96991ed06abdc92e6d7835632845e3a5"
|
||||
}
|
||||
|
||||
Generated
+19
-225
@@ -69,9 +69,6 @@ importers:
|
||||
esno:
|
||||
specifier: ^4.7.0
|
||||
version: 4.7.0
|
||||
lint-staged:
|
||||
specifier: ^15.2.2
|
||||
version: 15.2.2
|
||||
nodemon:
|
||||
specifier: ^3.1.4
|
||||
version: 3.1.4
|
||||
@@ -1039,10 +1036,6 @@ packages:
|
||||
ajv@6.12.6:
|
||||
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||
|
||||
ansi-escapes@6.2.1:
|
||||
resolution: {integrity: sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==}
|
||||
engines: {node: '>=14.16'}
|
||||
|
||||
ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1233,14 +1226,6 @@ packages:
|
||||
resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
cli-cursor@4.0.0:
|
||||
resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
cli-truncate@4.0.0:
|
||||
resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
cliui@8.0.1:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -1271,9 +1256,6 @@ packages:
|
||||
colord@2.9.3:
|
||||
resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
|
||||
|
||||
colorette@2.0.20:
|
||||
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
|
||||
|
||||
colorspace@1.1.4:
|
||||
resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==}
|
||||
|
||||
@@ -1281,10 +1263,6 @@ packages:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
commander@11.1.0:
|
||||
resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
commander@7.2.0:
|
||||
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -1506,9 +1484,6 @@ packages:
|
||||
elliptic@6.5.4:
|
||||
resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==}
|
||||
|
||||
emoji-regex@10.3.0:
|
||||
resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==}
|
||||
|
||||
emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
@@ -1770,9 +1745,6 @@ packages:
|
||||
eventemitter3@4.0.7:
|
||||
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
||||
|
||||
eventemitter3@5.0.1:
|
||||
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||
|
||||
execa@8.0.1:
|
||||
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
|
||||
engines: {node: '>=16.17'}
|
||||
@@ -1893,10 +1865,6 @@ packages:
|
||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||
engines: {node: 6.* || 8.* || >= 10.*}
|
||||
|
||||
get-east-asian-width@1.2.0:
|
||||
resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
get-func-name@2.0.2:
|
||||
resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
|
||||
|
||||
@@ -2083,14 +2051,6 @@ packages:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-fullwidth-code-point@4.0.0:
|
||||
resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
is-fullwidth-code-point@5.0.0:
|
||||
resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -2200,10 +2160,6 @@ packages:
|
||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
lilconfig@3.0.0:
|
||||
resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
lilconfig@3.1.1:
|
||||
resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -2211,15 +2167,6 @@ packages:
|
||||
lines-and-columns@1.2.4:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
|
||||
lint-staged@15.2.2:
|
||||
resolution: {integrity: sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
hasBin: true
|
||||
|
||||
listr2@8.0.1:
|
||||
resolution: {integrity: sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
local-pkg@0.5.0:
|
||||
resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -2257,10 +2204,6 @@ packages:
|
||||
lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
|
||||
log-update@6.0.0:
|
||||
resolution: {integrity: sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
logform@2.6.0:
|
||||
resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
@@ -2346,10 +2289,6 @@ packages:
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
|
||||
mimic-fn@2.1.0:
|
||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
mimic-fn@4.0.0:
|
||||
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2546,10 +2485,6 @@ packages:
|
||||
one-time@1.0.0:
|
||||
resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==}
|
||||
|
||||
onetime@5.1.2:
|
||||
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
onetime@6.0.0:
|
||||
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2682,11 +2617,6 @@ packages:
|
||||
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pidtree@0.6.0:
|
||||
resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
|
||||
engines: {node: '>=0.10'}
|
||||
hasBin: true
|
||||
|
||||
pkg-types@1.1.0:
|
||||
resolution: {integrity: sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==}
|
||||
|
||||
@@ -2976,17 +2906,10 @@ packages:
|
||||
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
|
||||
hasBin: true
|
||||
|
||||
restore-cursor@4.0.0:
|
||||
resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
reusify@1.0.4:
|
||||
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
||||
rfdc@1.3.1:
|
||||
resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==}
|
||||
|
||||
rimraf@3.0.2:
|
||||
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
||||
hasBin: true
|
||||
@@ -3085,9 +3008,6 @@ packages:
|
||||
siginfo@2.0.0:
|
||||
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||
|
||||
signal-exit@3.0.7:
|
||||
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||
|
||||
signal-exit@4.1.0:
|
||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -3118,14 +3038,6 @@ packages:
|
||||
resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==}
|
||||
engines: {node: '>=14.16'}
|
||||
|
||||
slice-ansi@5.0.0:
|
||||
resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
slice-ansi@7.1.0:
|
||||
resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
source-map-js@1.2.0:
|
||||
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3179,10 +3091,6 @@ packages:
|
||||
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
string-width@7.1.0:
|
||||
resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
string_decoder@1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
|
||||
@@ -3558,10 +3466,6 @@ packages:
|
||||
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
wrap-ansi@9.0.0:
|
||||
resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
@@ -3595,10 +3499,6 @@ packages:
|
||||
resolution: {integrity: sha512-pEwzfsKbTrB8G3xc/sN7aw1v6A6c/pKxLAkjclnAyo5g5qOh6eL9WGu0o3cSDQZKrTNk4KL4lQSwZW+nBkANEg==}
|
||||
engines: {node: ^14.17.0 || >=16.0.0}
|
||||
|
||||
yaml@2.3.4:
|
||||
resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
yaml@2.4.2:
|
||||
resolution: {integrity: sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==}
|
||||
engines: {node: '>= 14'}
|
||||
@@ -3703,7 +3603,7 @@ snapshots:
|
||||
'@babel/traverse': 7.24.1
|
||||
'@babel/types': 7.24.0
|
||||
convert-source-map: 2.0.0
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
gensync: 1.0.0-beta.2
|
||||
json5: 2.2.3
|
||||
semver: 6.3.1
|
||||
@@ -3804,7 +3704,7 @@ snapshots:
|
||||
'@babel/helper-split-export-declaration': 7.22.6
|
||||
'@babel/parser': 7.24.4
|
||||
'@babel/types': 7.24.0
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
globals: 11.12.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -4013,7 +3913,7 @@ snapshots:
|
||||
'@eslint/eslintrc@2.1.4':
|
||||
dependencies:
|
||||
ajv: 6.12.6
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
espree: 9.6.1
|
||||
globals: 13.24.0
|
||||
ignore: 5.3.1
|
||||
@@ -4029,7 +3929,7 @@ snapshots:
|
||||
'@humanwhocodes/config-array@0.11.14':
|
||||
dependencies:
|
||||
'@humanwhocodes/object-schema': 2.0.3
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
minimatch: 3.1.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -4348,7 +4248,7 @@ snapshots:
|
||||
'@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/visitor-keys': 7.7.1
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
eslint: 8.57.0
|
||||
graphemer: 1.4.0
|
||||
ignore: 5.3.1
|
||||
@@ -4366,7 +4266,7 @@ snapshots:
|
||||
'@typescript-eslint/types': 7.7.1
|
||||
'@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
|
||||
'@typescript-eslint/visitor-keys': 7.7.1
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
eslint: 8.57.0
|
||||
optionalDependencies:
|
||||
typescript: 5.4.5
|
||||
@@ -4387,7 +4287,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
|
||||
'@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
eslint: 8.57.0
|
||||
ts-api-utils: 1.3.0(typescript@5.4.5)
|
||||
optionalDependencies:
|
||||
@@ -4403,7 +4303,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 6.21.0
|
||||
'@typescript-eslint/visitor-keys': 6.21.0
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.3
|
||||
@@ -4418,7 +4318,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 7.7.1
|
||||
'@typescript-eslint/visitor-keys': 7.7.1
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.4
|
||||
@@ -4550,8 +4450,6 @@ snapshots:
|
||||
json-schema-traverse: 0.4.1
|
||||
uri-js: 4.4.1
|
||||
|
||||
ansi-escapes@6.2.1: {}
|
||||
|
||||
ansi-regex@5.0.1: {}
|
||||
|
||||
ansi-regex@6.0.1: {}
|
||||
@@ -4762,15 +4660,6 @@ snapshots:
|
||||
dependencies:
|
||||
escape-string-regexp: 1.0.5
|
||||
|
||||
cli-cursor@4.0.0:
|
||||
dependencies:
|
||||
restore-cursor: 4.0.0
|
||||
|
||||
cli-truncate@4.0.0:
|
||||
dependencies:
|
||||
slice-ansi: 5.0.0
|
||||
string-width: 7.1.0
|
||||
|
||||
cliui@8.0.1:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
@@ -4803,8 +4692,6 @@ snapshots:
|
||||
|
||||
colord@2.9.3: {}
|
||||
|
||||
colorette@2.0.20: {}
|
||||
|
||||
colorspace@1.1.4:
|
||||
dependencies:
|
||||
color: 3.2.1
|
||||
@@ -4814,8 +4701,6 @@ snapshots:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
commander@11.1.0: {}
|
||||
|
||||
commander@7.2.0: {}
|
||||
|
||||
comment-parser@1.4.1: {}
|
||||
@@ -4952,10 +4837,6 @@ snapshots:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
debug@4.3.4:
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
|
||||
debug@4.3.4(supports-color@5.5.0):
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
@@ -5040,8 +4921,6 @@ snapshots:
|
||||
minimalistic-assert: 1.0.1
|
||||
minimalistic-crypto-utils: 1.0.1
|
||||
|
||||
emoji-regex@10.3.0: {}
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
emoji-regex@9.2.2: {}
|
||||
@@ -5185,7 +5064,7 @@ snapshots:
|
||||
eslint-plugin-import-x@0.5.0(eslint@8.57.0)(typescript@5.4.5):
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
doctrine: 3.0.0
|
||||
eslint: 8.57.0
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
@@ -5202,7 +5081,7 @@ snapshots:
|
||||
'@es-joy/jsdoccomment': 0.42.0
|
||||
are-docs-informative: 0.0.2
|
||||
comment-parser: 1.4.1
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint: 8.57.0
|
||||
esquery: 1.5.0
|
||||
@@ -5258,7 +5137,7 @@ snapshots:
|
||||
|
||||
eslint-plugin-toml@0.11.0(eslint@8.57.0):
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
eslint: 8.57.0
|
||||
eslint-compat-utils: 0.5.0(eslint@8.57.0)
|
||||
lodash: 4.17.21
|
||||
@@ -5322,7 +5201,7 @@ snapshots:
|
||||
|
||||
eslint-plugin-yml@1.14.0(eslint@8.57.0):
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
eslint: 8.57.0
|
||||
eslint-compat-utils: 0.5.0(eslint@8.57.0)
|
||||
lodash: 4.17.21
|
||||
@@ -5358,7 +5237,7 @@ snapshots:
|
||||
ajv: 6.12.6
|
||||
chalk: 4.1.2
|
||||
cross-spawn: 7.0.3
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
doctrine: 3.0.0
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint-scope: 7.2.2
|
||||
@@ -5420,8 +5299,6 @@ snapshots:
|
||||
|
||||
eventemitter3@4.0.7: {}
|
||||
|
||||
eventemitter3@5.0.1: {}
|
||||
|
||||
execa@8.0.1:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.3
|
||||
@@ -5587,8 +5464,6 @@ snapshots:
|
||||
|
||||
get-caller-file@2.0.5: {}
|
||||
|
||||
get-east-asian-width@1.2.0: {}
|
||||
|
||||
get-func-name@2.0.2: {}
|
||||
|
||||
get-intrinsic@1.2.4:
|
||||
@@ -5760,7 +5635,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@ioredis/commands': 1.2.0
|
||||
cluster-key-slot: 1.1.2
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
denque: 2.1.0
|
||||
lodash.defaults: 4.2.0
|
||||
lodash.isarguments: 3.1.0
|
||||
@@ -5801,12 +5676,6 @@ snapshots:
|
||||
|
||||
is-fullwidth-code-point@3.0.0: {}
|
||||
|
||||
is-fullwidth-code-point@4.0.0: {}
|
||||
|
||||
is-fullwidth-code-point@5.0.0:
|
||||
dependencies:
|
||||
get-east-asian-width: 1.2.0
|
||||
|
||||
is-glob@4.0.3:
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
@@ -5891,36 +5760,10 @@ snapshots:
|
||||
prelude-ls: 1.2.1
|
||||
type-check: 0.4.0
|
||||
|
||||
lilconfig@3.0.0: {}
|
||||
|
||||
lilconfig@3.1.1: {}
|
||||
|
||||
lines-and-columns@1.2.4: {}
|
||||
|
||||
lint-staged@15.2.2:
|
||||
dependencies:
|
||||
chalk: 5.3.0
|
||||
commander: 11.1.0
|
||||
debug: 4.3.4
|
||||
execa: 8.0.1
|
||||
lilconfig: 3.0.0
|
||||
listr2: 8.0.1
|
||||
micromatch: 4.0.5
|
||||
pidtree: 0.6.0
|
||||
string-argv: 0.3.2
|
||||
yaml: 2.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
listr2@8.0.1:
|
||||
dependencies:
|
||||
cli-truncate: 4.0.0
|
||||
colorette: 2.0.20
|
||||
eventemitter3: 5.0.1
|
||||
log-update: 6.0.0
|
||||
rfdc: 1.3.1
|
||||
wrap-ansi: 9.0.0
|
||||
|
||||
local-pkg@0.5.0:
|
||||
dependencies:
|
||||
mlly: 1.6.1
|
||||
@@ -5952,14 +5795,6 @@ snapshots:
|
||||
|
||||
lodash@4.17.21: {}
|
||||
|
||||
log-update@6.0.0:
|
||||
dependencies:
|
||||
ansi-escapes: 6.2.1
|
||||
cli-cursor: 4.0.0
|
||||
slice-ansi: 7.1.0
|
||||
strip-ansi: 7.1.0
|
||||
wrap-ansi: 9.0.0
|
||||
|
||||
logform@2.6.0:
|
||||
dependencies:
|
||||
'@colors/colors': 1.6.0
|
||||
@@ -6023,7 +5858,7 @@ snapshots:
|
||||
|
||||
micromark@2.11.4:
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
parse-entities: 2.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -6041,8 +5876,6 @@ snapshots:
|
||||
|
||||
mime@1.6.0: {}
|
||||
|
||||
mimic-fn@2.1.0: {}
|
||||
|
||||
mimic-fn@4.0.0: {}
|
||||
|
||||
min-indent@1.0.1: {}
|
||||
@@ -6210,10 +6043,6 @@ snapshots:
|
||||
dependencies:
|
||||
fn.name: 1.1.0
|
||||
|
||||
onetime@5.1.2:
|
||||
dependencies:
|
||||
mimic-fn: 2.1.0
|
||||
|
||||
onetime@6.0.0:
|
||||
dependencies:
|
||||
mimic-fn: 4.0.0
|
||||
@@ -6329,8 +6158,6 @@ snapshots:
|
||||
|
||||
picomatch@4.0.2: {}
|
||||
|
||||
pidtree@0.6.0: {}
|
||||
|
||||
pkg-types@1.1.0:
|
||||
dependencies:
|
||||
confbox: 0.1.7
|
||||
@@ -6601,15 +6428,8 @@ snapshots:
|
||||
path-parse: 1.0.7
|
||||
supports-preserve-symlinks-flag: 1.0.0
|
||||
|
||||
restore-cursor@4.0.0:
|
||||
dependencies:
|
||||
onetime: 5.1.2
|
||||
signal-exit: 3.0.7
|
||||
|
||||
reusify@1.0.4: {}
|
||||
|
||||
rfdc@1.3.1: {}
|
||||
|
||||
rimraf@3.0.2:
|
||||
dependencies:
|
||||
glob: 7.2.3
|
||||
@@ -6742,8 +6562,6 @@ snapshots:
|
||||
|
||||
siginfo@2.0.0: {}
|
||||
|
||||
signal-exit@3.0.7: {}
|
||||
|
||||
signal-exit@4.1.0: {}
|
||||
|
||||
simple-git-hooks@2.11.1: {}
|
||||
@@ -6764,16 +6582,6 @@ snapshots:
|
||||
|
||||
slash@5.1.0: {}
|
||||
|
||||
slice-ansi@5.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.1
|
||||
is-fullwidth-code-point: 4.0.0
|
||||
|
||||
slice-ansi@7.1.0:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.1
|
||||
is-fullwidth-code-point: 5.0.0
|
||||
|
||||
source-map-js@1.2.0: {}
|
||||
|
||||
sparse-bitfield@3.0.3:
|
||||
@@ -6825,12 +6633,6 @@ snapshots:
|
||||
emoji-regex: 9.2.2
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
string-width@7.1.0:
|
||||
dependencies:
|
||||
emoji-regex: 10.3.0
|
||||
get-east-asian-width: 1.2.0
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
string_decoder@1.3.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
@@ -7087,7 +6889,7 @@ snapshots:
|
||||
vite-node@1.5.2(@types/node@20.12.7):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
pathe: 1.1.2
|
||||
picocolors: 1.0.0
|
||||
vite: 5.2.10(@types/node@20.12.7)
|
||||
@@ -7119,7 +6921,7 @@ snapshots:
|
||||
'@vitest/utils': 1.5.2
|
||||
acorn-walk: 8.3.2
|
||||
chai: 4.4.1
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
execa: 8.0.1
|
||||
local-pkg: 0.5.0
|
||||
magic-string: 0.30.10
|
||||
@@ -7149,7 +6951,7 @@ snapshots:
|
||||
|
||||
vue-eslint-parser@9.4.2(eslint@8.57.0):
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
debug: 4.3.4(supports-color@5.5.0)
|
||||
eslint: 8.57.0
|
||||
eslint-scope: 7.2.2
|
||||
eslint-visitor-keys: 3.4.3
|
||||
@@ -7219,12 +7021,6 @@ snapshots:
|
||||
string-width: 5.1.2
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
wrap-ansi@9.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 6.2.1
|
||||
string-width: 7.1.0
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
|
||||
ws@7.5.9: {}
|
||||
@@ -7243,8 +7039,6 @@ snapshots:
|
||||
lodash: 4.17.21
|
||||
yaml: 2.4.2
|
||||
|
||||
yaml@2.3.4: {}
|
||||
|
||||
yaml@2.4.2: {}
|
||||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
@@ -15,10 +15,9 @@ export async function BlockParser(db: Database, reader: EosioShipReaderResolved)
|
||||
})
|
||||
|
||||
blocks$.subscribe(async (block: IBlock) => {
|
||||
// console.log('new block: ', block)
|
||||
// process.stdout.write('\r') // Возврат каретки в начало строки
|
||||
// process.stdout.write(`Блок: ${block.block_num}`) // Ваш текст
|
||||
|
||||
db.updateCurrentBlock(block.last_irreversible_block_num)
|
||||
db.updateCurrentBlock(block.block_num)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export class Database {
|
||||
|
||||
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')
|
||||
@@ -51,18 +51,15 @@ export class Database {
|
||||
|
||||
const pipeline = [
|
||||
{ $match: filter },
|
||||
{ $sort: { block_num: -1, _id: -1 } },
|
||||
{ $sort: { block_num: -1 } }, // Сортировка по primary_key и block_num
|
||||
{ $group: { _id: '$primary_key', doc: { $first: '$$ROOT' } } },
|
||||
// { $match: { 'doc.present': true } },
|
||||
{ $replaceRoot: { newRoot: '$doc' } },
|
||||
{ $sort: { block_num: -1 } },
|
||||
{ $skip: (page - 1) * limit }, // Применяется внутри пайплайна
|
||||
{ $limit: limit }, // Применяется внутри пайплайна
|
||||
]
|
||||
|
||||
const [result] = await Promise.all([
|
||||
this.deltas.aggregate(pipeline)
|
||||
.skip((page - 1) * limit)
|
||||
.limit(limit)
|
||||
.toArray(),
|
||||
])
|
||||
const result = await this.deltas.aggregate(pipeline).toArray()
|
||||
|
||||
return {
|
||||
results: result as IDelta[],
|
||||
@@ -79,9 +76,10 @@ export class Database {
|
||||
|
||||
const result = await this.actions.aggregate([
|
||||
{ $match: query },
|
||||
{ $sort: { block_num: -1, _id: -1 } },
|
||||
{ $sort: { block_num: -1 } }, // Сортировка по primary_key и block_num
|
||||
{ $group: { _id: '$global_sequence', doc: { $first: '$$ROOT' } } },
|
||||
{ $replaceRoot: { newRoot: '$doc' } },
|
||||
{ $sort: { block_num: -1 } },
|
||||
{ $skip: (page - 1) * limit },
|
||||
{ $limit: limit },
|
||||
]).toArray()
|
||||
|
||||
@@ -10,10 +10,10 @@ function getEnvVar(key: string): string {
|
||||
|
||||
return envVar
|
||||
}
|
||||
|
||||
export const node_env = getEnvVar('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')
|
||||
@@ -54,6 +54,7 @@ export const subsribedActions: IActionConfig[] = [
|
||||
{ code: 'soviet', action: 'joincoopdec' },
|
||||
|
||||
{ code: 'soviet', action: 'updateboard', notify: true },
|
||||
{ code: 'soviet', action: 'createboard', notify: true },
|
||||
]
|
||||
|
||||
// --------------------------
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -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": "warn",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -12,4 +12,7 @@ yarn-error.log
|
||||
coverage
|
||||
|
||||
.env-mixed
|
||||
.env-local
|
||||
.env-local
|
||||
|
||||
logs
|
||||
logs/*
|
||||
|
||||
@@ -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
|
||||
|
||||
+4
-2
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"CodeGPT.apiKey": "OpenAI",
|
||||
"cSpell.words": [
|
||||
"subcode"
|
||||
]
|
||||
],
|
||||
"editor.formatOnSave": true,
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,146 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.1.3](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.2-alpha.10...v2.1.3) (2024-10-26)
|
||||
|
||||
|
||||
|
||||
## [2.1.2](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.1...v2.1.2) (2024-10-19)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.2](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.1...v2.1.2) (2024-10-19)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.1.0](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.10-alpha.3...v2.1.0) (2024-10-13)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.8](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.7...v2.0.8) (2024-10-09)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.7](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.6...v2.0.7) (2024-09-30)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.6](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.5...v2.0.6) (2024-09-30)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.5](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.5-alpha.0...v2.0.5) (2024-09-30)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.0.2](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.2-alpha.1...v2.0.2) (2024-09-29)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.38](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.38-alpha.3...coopback@1.7.38) (2024-09-29)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.34](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.33...coopback@1.7.34) (2024-08-25)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.33](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.32...coopback@1.7.33) (2024-08-25)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.32](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.31...coopback@1.7.32) (2024-08-25)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.31](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.31-alpha.1...coopback@1.7.31) (2024-08-25)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.30](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.29...coopback@1.7.30) (2024-08-13)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.7.29](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.29-alpha.0...coopback@1.7.29) (2024-08-05)
|
||||
|
||||
**Note:** Version bump only for package coopback
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# COOPERATIVE CONTROLLER
|
||||
# COOPERATIVE CONTROLLER.
|
||||
|
||||
[](https://travis-ci.org/hagopj13/node-express-boilerplate)
|
||||
[](https://coveralls.io/github/hagopj13/node-express-boilerplate?branch=master)
|
||||
@@ -8,6 +8,7 @@ A boilerplate/starter project for quickly building RESTful APIs using Node.js, E
|
||||
|
||||
By running a single command, you will get a production-ready Node.js app installed and fully configured on your machine. The app comes with many built-in features, such as authentication using JWT, request validation, unit and integration tests, continuous integration, docker support, API documentation, pagination, etc. For more details, check the features list below.
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
||||
To create a project, simply run:
|
||||
|
||||
Generated
+4
-5
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "create-nodejs-express-app",
|
||||
"version": "1.7.26",
|
||||
"version": "2.1.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "create-nodejs-express-app",
|
||||
"version": "1.7.26",
|
||||
"version": "2.1.3",
|
||||
"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,22 +1,26 @@
|
||||
{
|
||||
"name": "coopback",
|
||||
"version": "1.7.26",
|
||||
"name": "@coopenomics/controller",
|
||||
"version": "2.1.3",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"private": false,
|
||||
"bin": "bin/createNodejsApp.js",
|
||||
"main": "src/index.ts",
|
||||
"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"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"deploy-testnet": "git checkout testnet && git merge main && git push origin testnet && git checkout main",
|
||||
"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 +32,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",
|
||||
@@ -78,16 +84,21 @@
|
||||
"helmet": "^4.1.0",
|
||||
"html-pdf-node": "^1.0.8",
|
||||
"http-status": "^1.4.0",
|
||||
"ioredis": "^5.4.1",
|
||||
"ip": "^2.0.1",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"joi": "^17.13.1",
|
||||
"joi-to-json": "^4.3.0",
|
||||
"joi-to-typescript": "^4.13.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jspdf": "^2.5.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.24.0",
|
||||
"mongoose": "^5.7.7",
|
||||
"mongoose": "^8.5.2",
|
||||
"mongoose-sequence": "^6.0.1",
|
||||
"morgan": "^1.9.1",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^6.3.1",
|
||||
"nunjucks": "^3.2.4",
|
||||
"passport": "^0.4.0",
|
||||
@@ -97,26 +108,33 @@
|
||||
"socket.io-client": "^4.7.5",
|
||||
"swagger-jsdoc": "^6.0.8",
|
||||
"swagger-ui-express": "^4.1.6",
|
||||
"threads": "^1.7.0",
|
||||
"ts-jest": "^29.1.3",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsd": "^0.31.0",
|
||||
"tsoa": "^6.4.0",
|
||||
"validator": "^13.0.0",
|
||||
"winston": "^3.2.1",
|
||||
"winston-daily-rotate-file": "^5.0.0",
|
||||
"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);
|
||||
}
|
||||
|
||||
@@ -16,12 +17,11 @@ const [username] = args;
|
||||
async function createServiceUser(username: string) {
|
||||
try {
|
||||
await mongoose.connect(config.mongoose.url, config.mongoose.options);
|
||||
|
||||
const user = await userService.createServiceUser(username);
|
||||
const token = await tokenService.generateServiceAccessToken(user);
|
||||
console.log('token:', token.access.token);
|
||||
} catch (e: any) {
|
||||
console.log('Ошибка: ', e.message);
|
||||
logger.error('Ошибка: ', e.message);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
|
||||
@@ -17,7 +17,7 @@ async function deleteUser(username: string) {
|
||||
try {
|
||||
await mongoose.connect(config.mongoose.url, config.mongoose.options);
|
||||
|
||||
await userService.deleteUserById(username);
|
||||
await userService.deleteUserByUsername(username);
|
||||
|
||||
console.log('Пользователь удалён: ', username);
|
||||
} catch (e: any) {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
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';
|
||||
|
||||
await userService.createUser({
|
||||
username: 'ant',
|
||||
email: 'dacom.dark.sun@gmail.com',
|
||||
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
|
||||
role: 'user',
|
||||
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',
|
||||
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,
|
||||
});
|
||||
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -14,16 +14,6 @@ import routes from './routes/v1/index';
|
||||
import { errorConverter, errorHandler } from './middlewares/error';
|
||||
import ApiError from './utils/ApiError';
|
||||
|
||||
import { connectGenerator } from './services/data.service';
|
||||
// Подключите генератор
|
||||
connectGenerator()
|
||||
.then(() => {
|
||||
console.log('Generator connected');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to connect generator:', error);
|
||||
});
|
||||
|
||||
const app = express();
|
||||
|
||||
if (config.env !== 'test') {
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
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(),
|
||||
SERVER_SECRET: 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'),
|
||||
@@ -23,11 +20,16 @@ const envVarsSchema = Joi.object()
|
||||
JWT_VERIFY_EMAIL_EXPIRATION_MINUTES: Joi.number()
|
||||
.default(10)
|
||||
.description('minutes after which verify email token expires'),
|
||||
|
||||
JWT_INVITE_EXPIRATION_MINUTES: Joi.number().default(3600).description('minutes after invite token expires'),
|
||||
|
||||
SMTP_HOST: Joi.string().description('server that will send the emails'),
|
||||
SMTP_PORT: Joi.number().description('port to connect to the email server'),
|
||||
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 +41,15 @@ if (error) {
|
||||
|
||||
export default {
|
||||
env: envVars.NODE_ENV,
|
||||
base_url: envVars.BASE_URL,
|
||||
port: envVars.PORT,
|
||||
server_secret: envVars.SERVER_SECRET,
|
||||
mongoose: {
|
||||
url: envVars.MONGODB_URL + (envVars.NODE_ENV === 'test' ? '-test' : ''),
|
||||
options: {
|
||||
useCreateIndex: true,
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
// useCreateIndex: true,
|
||||
// useNewUrlParser: true,
|
||||
// useUnifiedTopology: true,
|
||||
},
|
||||
},
|
||||
jwt: {
|
||||
@@ -54,6 +58,7 @@ export default {
|
||||
refreshExpirationDays: envVars.JWT_REFRESH_EXPIRATION_DAYS,
|
||||
resetPasswordExpirationMinutes: envVars.JWT_RESET_PASSWORD_EXPIRATION_MINUTES,
|
||||
verifyEmailExpirationMinutes: envVars.JWT_VERIFY_EMAIL_EXPIRATION_MINUTES,
|
||||
inviteExpirationMinutes: envVars.JWT_INVITE_EXPIRATION_MINUTES,
|
||||
},
|
||||
email: {
|
||||
smtp: {
|
||||
@@ -66,4 +71,5 @@ export default {
|
||||
},
|
||||
from: envVars.EMAIL_FROM,
|
||||
},
|
||||
coopname: envVars.COOPNAME,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import winston from 'winston'
|
||||
import config from './config'
|
||||
import winston from 'winston';
|
||||
import DailyRotateFile from 'winston-daily-rotate-file';
|
||||
import config from './config';
|
||||
|
||||
const enumerateErrorFormat = winston.format((info) => {
|
||||
if (info instanceof Error) {
|
||||
@@ -11,14 +12,34 @@ const enumerateErrorFormat = winston.format((info) => {
|
||||
const logger = winston.createLogger({
|
||||
level: config.env === 'development' ? 'debug' : 'info',
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
enumerateErrorFormat(),
|
||||
config.env === 'development' ? winston.format.colorize() : winston.format.uncolorize(),
|
||||
winston.format.colorize(),
|
||||
winston.format.splat(),
|
||||
winston.format.printf(({ level, message }) => `${level}: ${message}`)
|
||||
winston.format.printf(({ timestamp, level, message, ...meta }) => {
|
||||
return `${timestamp} ${level}: ${message}${Object.keys(meta).length ? ` - ${JSON.stringify(meta)}` : ''}`;
|
||||
})
|
||||
),
|
||||
transports: [
|
||||
new DailyRotateFile({
|
||||
filename: 'logs/application-%DATE%.log',
|
||||
datePattern: 'YYYY-MM-DD',
|
||||
zippedArchive: true,
|
||||
maxSize: '20m',
|
||||
maxFiles: '30d',
|
||||
level: 'info',
|
||||
}),
|
||||
new DailyRotateFile({
|
||||
filename: 'logs/error-%DATE%.log',
|
||||
datePattern: 'YYYY-MM-DD',
|
||||
zippedArchive: true,
|
||||
maxSize: '20m',
|
||||
maxFiles: '30d',
|
||||
level: 'error',
|
||||
}),
|
||||
new winston.transports.Console({
|
||||
stderrLevels: ['error'],
|
||||
level: 'info',
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import morgan from 'morgan'
|
||||
import config from './config'
|
||||
import logger from './logger'
|
||||
import morgan from 'morgan';
|
||||
import config from './config';
|
||||
import logger from './logger';
|
||||
|
||||
morgan.token('message', (req, res) => res.locals.errorMessage || '');
|
||||
|
||||
@@ -21,4 +21,4 @@ const errorHandler = morgan(errorResponseFormat, {
|
||||
export default {
|
||||
successHandler,
|
||||
errorHandler,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import {Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt'
|
||||
import config from './config'
|
||||
import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt';
|
||||
import config from './config';
|
||||
import { tokenTypes } from './tokens';
|
||||
import { User } from '../models';
|
||||
|
||||
|
||||
const jwtOptions = {
|
||||
secretOrKey: config.jwt.secret,
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
@@ -14,7 +13,7 @@ const jwtVerify = async (payload, done) => {
|
||||
if (payload.type !== tokenTypes.ACCESS) {
|
||||
throw new Error('Invalid token type');
|
||||
}
|
||||
const user = await User.findOne({_id: payload.sub});
|
||||
const user = await User.findOne({ _id: payload.sub });
|
||||
if (!user) {
|
||||
return done(null, false);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,33 @@
|
||||
const user = [
|
||||
'getSelf',
|
||||
'initialPayment',
|
||||
'getMyDocuments',
|
||||
'getMyOrders',
|
||||
'sendVerificationEmail',
|
||||
'manageMyMethods',
|
||||
'createDeposit',
|
||||
'joinCooperative',
|
||||
'getVars',
|
||||
];
|
||||
|
||||
const member = [
|
||||
...user,
|
||||
'getUsers',
|
||||
'manageUsers',
|
||||
'loadAgenda',
|
||||
'loadStaff',
|
||||
'getDocuments',
|
||||
'loadInfo',
|
||||
'manageOrders',
|
||||
'regenerateDocument',
|
||||
];
|
||||
|
||||
const chairman = [...user, ...member, 'addUser', 'setVars', 'manageSettings', 'setWif', 'setPlugin'];
|
||||
|
||||
const allRoles = {
|
||||
user: [''],
|
||||
service: ['sendNotification'],
|
||||
chairman: ['getUsers', 'manageUsers', 'loadAgenda', 'loadStaff', 'getDocuments', 'loadInfo'],
|
||||
member: ['getUsers', 'manageUsers', 'loadAgenda', 'loadStaff', 'getDocuments', 'loadInfo'],
|
||||
user,
|
||||
member,
|
||||
chairman,
|
||||
};
|
||||
|
||||
export const roles = Object.keys(allRoles);
|
||||
|
||||
@@ -3,4 +3,5 @@ export const tokenTypes = {
|
||||
REFRESH: 'refresh',
|
||||
RESET_PASSWORD: 'resetPassword',
|
||||
VERIFY_EMAIL: 'verifyEmail',
|
||||
INVITE: 'invite',
|
||||
};
|
||||
|
||||
@@ -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.generateResetKeyToken(req.body.email);
|
||||
await emailService.sendResetKeyEmail(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[] = [];
|
||||
const complexAgenda: Cooperative.Document.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 type = req.query?.type as string;
|
||||
const documents = await documentService.queryDocuments(type, filter, page, limit);
|
||||
|
||||
res.status(httpStatus.OK).send(documents);
|
||||
});
|
||||
@@ -1,6 +1,11 @@
|
||||
export * as authController from './auth.controller';
|
||||
export * as userController from './user.controller';
|
||||
export * as dataController from './data.controller';
|
||||
export * as documentController from './document.controller';
|
||||
export * as orderController from './order.controller';
|
||||
export * as coopController from './coop.controller';
|
||||
export * as notifyController from './notify.controller';
|
||||
export * as systemController from './system.controller';
|
||||
export * as participantController from './participant.controller';
|
||||
export * as methodController from './method.controller';
|
||||
export * as settingController from './setting.controller';
|
||||
export * as pluginController from './plugin.controller';
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import http from 'http-status';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { methodService } from '../services';
|
||||
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 type { RDeletePaymentMethod, RGetListPaymentMethods, RSavePaymentMethod } from '../types';
|
||||
|
||||
export const listPaymentMethods = catchAsync(async (req: RGetListPaymentMethods, res) => {
|
||||
const filter = req.params?.username ? { username: req.params.username } : {};
|
||||
|
||||
const methods: IGetResponse<any> = await methodService.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,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
user_type: user.type as any,
|
||||
method_type: req.body.method_type,
|
||||
is_default: false,
|
||||
data: req.body.data,
|
||||
};
|
||||
|
||||
await methodService.savePaymentMethod(paymentData);
|
||||
|
||||
res.status(httpStatus.OK).send();
|
||||
});
|
||||
|
||||
export const deletePaymentMethod = catchAsync(async (req: RDeletePaymentMethod, res) => {
|
||||
await methodService.deletePaymentMethod({
|
||||
username: req.params?.username,
|
||||
method_id: req.body.method_id,
|
||||
});
|
||||
|
||||
res.status(httpStatus.OK).send();
|
||||
});
|
||||
@@ -3,56 +3,60 @@ 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')
|
||||
import httpStatus from 'http-status';
|
||||
import { IPNProviderFactory } from '../services/payment/ipn/ipnProviderFactory';
|
||||
import pick from '../utils/pick';
|
||||
import logger from '../config/logger';
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
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 providerName = req.params.provider;
|
||||
logger.info(`Recieve new IPN for provider ${providerName}`, { body: req.body, source: 'catchIPN' });
|
||||
|
||||
// const isAllowed = allowedIPs.some(allowedIP => {
|
||||
// if (allowedIP.includes('/')) { // Если это диапазон
|
||||
// return ip.cidrSubnet(allowedIP).contains(ipAddr);
|
||||
// }
|
||||
// return allowedIP === ipAddr; // Если это конкретный IP
|
||||
// });
|
||||
const provider = IPNProviderFactory.createProvider(providerName);
|
||||
|
||||
// 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);
|
||||
// Обрабатываем IPN данные
|
||||
await provider.handleIPN(req.body);
|
||||
res.status(200).send();
|
||||
});
|
||||
|
||||
|
||||
export const createDeposit = catchAsync(async (req: any, res: Response) => {
|
||||
const username = req.user.username
|
||||
const username = req.user.username;
|
||||
|
||||
const result = await orderService.createDeposit(username, req.body as ICreateDeposit)
|
||||
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)
|
||||
const username = req.user.username;
|
||||
const result = await orderService.createInitialOrder(username);
|
||||
res.status(http.CREATED).send(result);
|
||||
})
|
||||
});
|
||||
|
||||
export const getOrders = catchAsync(async (req: Request, res: Response) => {
|
||||
const filter = pick(req.query, ['username', 'id', 'order_num', 'status']);
|
||||
const options = pick(req.query, ['sortBy', 'limit', 'page']);
|
||||
|
||||
const orders = await orderService.getOrders(filter, options);
|
||||
res.status(httpStatus.OK).send(orders);
|
||||
});
|
||||
|
||||
export const getMyOrders = catchAsync(async (req: Request, res: Response) => {
|
||||
console.log(req.params);
|
||||
|
||||
const filter = pick(req.query, ['status', 'id']);
|
||||
const options = pick(req.query, ['sortBy', 'limit', 'page']);
|
||||
|
||||
filter.username = req.params.username;
|
||||
|
||||
const orders = await orderService.getOrders(filter, options);
|
||||
res.status(httpStatus.OK).send(orders);
|
||||
});
|
||||
|
||||
export const setStatus = catchAsync(async (req: Request, res: Response) => {
|
||||
const { status, id } = req.body;
|
||||
|
||||
await orderService.setStatus(id, status);
|
||||
|
||||
res.status(httpStatus.OK).send();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import httpStatus from 'http-status';
|
||||
import { participantService } from '../services';
|
||||
import type { RJoinCooperative } from '../types';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
|
||||
export const joinCooperative = catchAsync(async (req: RJoinCooperative, res) => {
|
||||
await participantService.joinCooperative(req.body);
|
||||
|
||||
res.status(httpStatus.OK).send();
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { pluginService } from '../services';
|
||||
import httpStatus from 'http-status';
|
||||
import type { RGetPluginConfig, RGetPluginList, RGetPluginSchema, RSetPlugin } from '../types';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { restartWorker, terminateWorker } from '../factories/pluginFactory';
|
||||
import pick from '../utils/pick';
|
||||
|
||||
export const getPluginList = catchAsync(async (req: RGetPluginList, res: Response) => {
|
||||
const filter = pick(req.query, ['name']);
|
||||
const options = pick(req.query, ['sortBy', 'limit', 'page']);
|
||||
|
||||
const plugins = await pluginService.queryPlugins(filter, options);
|
||||
|
||||
res.send(plugins);
|
||||
});
|
||||
|
||||
// Контроллер для обновления параметров плагинов
|
||||
export const setPluginConfig = catchAsync(async (req: RSetPlugin, res: Response) => {
|
||||
const { name, config, enabled } = req.body;
|
||||
try {
|
||||
const updatedConfig = await pluginService.updatePluginConfig(name, enabled, config);
|
||||
|
||||
if (enabled == false) await terminateWorker(name);
|
||||
else await restartWorker(name);
|
||||
|
||||
res.status(httpStatus.OK).json({
|
||||
message: `Конфигурация плагина ${name} успешно обновлена`,
|
||||
config: updatedConfig,
|
||||
});
|
||||
} catch (e: any) {
|
||||
throw new ApiError(httpStatus.BAD_REQUEST, e.message);
|
||||
}
|
||||
});
|
||||
|
||||
// Контроллер для обновления параметров плагинов
|
||||
export const getPluginSchema = catchAsync(async (req: RGetPluginSchema, res: Response) => {
|
||||
const { name } = req.query;
|
||||
try {
|
||||
const schema = pluginService.getPluginSchema(name);
|
||||
res.status(httpStatus.OK).json(schema);
|
||||
} catch (e: any) {
|
||||
throw new ApiError(httpStatus.BAD_REQUEST, e.message);
|
||||
}
|
||||
});
|
||||
|
||||
// Контроллер для обновления параметров плагинов
|
||||
export const getPluginConfig = catchAsync(async (req: RGetPluginConfig, res: Response) => {
|
||||
const { name } = req.query;
|
||||
console.log('plugin', name);
|
||||
const config = await pluginService.getPluginConfig(name);
|
||||
|
||||
res.status(httpStatus.OK).json(config);
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import { settingService } from '../services';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
export const updateSettings = catchAsync(async (req, res) => {
|
||||
const { settings } = req.body;
|
||||
|
||||
await settingService.updateSettings(settings);
|
||||
|
||||
res.status(httpStatus.OK).send();
|
||||
});
|
||||
|
||||
export const getSettings = catchAsync(async (req, res) => {
|
||||
const settings = await settingService.getSettings();
|
||||
|
||||
res.status(httpStatus.OK).send(settings);
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
import httpStatus from 'http-status';
|
||||
import { getMonoStatus } from '../services/system.service';
|
||||
import { getBlockchainInfo } from '../services/blockchain.service';
|
||||
import { IHealthResponse, IInstall, RInstall } from '../types';
|
||||
import { Request, Response } from 'express';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { systemService } from '../services';
|
||||
import type { IInit, ISetVars, RInit, RSetVars, RSetWif } from '../types/auto-generated/system.validation';
|
||||
import logger from '../config/logger';
|
||||
|
||||
export const init = catchAsync(async (req: RInit, res: Response) => {
|
||||
const { body } = req;
|
||||
await systemService.init(body as IInit);
|
||||
|
||||
res.status(httpStatus.OK).send();
|
||||
});
|
||||
|
||||
export const install = catchAsync(async (req: RInstall, res: Response) => {
|
||||
const { body } = req;
|
||||
await systemService.install(body as IInstall);
|
||||
|
||||
logger.info('System installed');
|
||||
|
||||
res.status(httpStatus.OK).send();
|
||||
});
|
||||
|
||||
export const setWif = catchAsync(async (req: RSetWif, res: Response) => {
|
||||
await systemService.setWif(req.body);
|
||||
res.status(httpStatus.OK).send();
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
export const setVars = catchAsync(async (req: RSetVars, res: Response) => {
|
||||
await systemService.setVars(req.body as ISetVars);
|
||||
res.status(httpStatus.OK).send();
|
||||
});
|
||||
|
||||
export const getVarsSchema = catchAsync(async (req: RSetVars, res: Response) => {
|
||||
const schema = await systemService.getVarsSchema();
|
||||
res.status(httpStatus.OK).send(schema);
|
||||
});
|
||||
|
||||
export const getVars = catchAsync(async (req: RSetVars, res: Response) => {
|
||||
const vars = await systemService.getVars();
|
||||
res.status(httpStatus.OK).send(vars);
|
||||
});
|
||||
@@ -1,11 +1,30 @@
|
||||
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, emailService, blockchainService } from '../services';
|
||||
import { IAddUser, ICreateUser, RCreateUser } from '../types';
|
||||
import httpStatus from 'http-status';
|
||||
import pick from '../utils/pick';
|
||||
import { IGetResponse } from '../types/common';
|
||||
// import { IGetResponse } from '../types/common';
|
||||
import { IUser, userStatus } from '../types';
|
||||
import { Request, Response } from 'express';
|
||||
import { generateUsername } from '../../tests/utils/generateUsername';
|
||||
import config from '../config/config';
|
||||
import logger from '../config/logger';
|
||||
// import { Body, Controller, Get, Path, Post, Query, Route, SuccessResponse } from 'tsoa';
|
||||
|
||||
// @Route('users')
|
||||
// export default class UsersController extends Controller {
|
||||
// @SuccessResponse(201, 'Created') // Указываем статус 201 и описание
|
||||
// @Post()
|
||||
// public async createUser(@Body() req: RCreateUser): Promise<{ user: IUser; tokens: any }> {
|
||||
// const user = await userService.createUser(req.body);
|
||||
// const tokens = await tokenService.generateAuthTokens(user);
|
||||
|
||||
// this.setStatus(201); // Устанавливаем статус 201 Created
|
||||
// return { user, tokens }; // Возвращаем объект в ответ
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Порядок регистрации:
|
||||
@@ -18,10 +37,11 @@ import { IGetResponse } from '../types/common';
|
||||
* 7. (joinCooperative) Пользователь подписывает заявление на вступление и вызывает метод joinCooperative, прикладывая подписанное заявление.
|
||||
* 8. Система сохраняет подписанное заявление
|
||||
* 9. (createInitialPayment) Пользователь получает ордер на оплату.
|
||||
* 9. Пользователь оплачивает ордер.
|
||||
* 10. Система принимает оплату и регистрирует аккаунт в блокчейне.
|
||||
* 11. (trx confirmpay) Пользователь подписывает велком-письмо с подтверждением собственноручности оплаты (транзакция в блокчейне для активации)
|
||||
* 12. Регистрация продолжается ожиданием решения совета
|
||||
*/
|
||||
|
||||
export const createUser = catchAsync(async (req: RCreateUser, res) => {
|
||||
const user = await userService.createUser(req.body);
|
||||
const tokens = await tokenService.generateAuthTokens(user);
|
||||
@@ -29,55 +49,67 @@ export const createUser = catchAsync(async (req: RCreateUser, res) => {
|
||||
res.status(http.CREATED).send({ user, tokens });
|
||||
});
|
||||
|
||||
export const joinCooperative = catchAsync(async (req: RJoinCooperative, res) => {
|
||||
await userService.joinCooperative(req.body);
|
||||
export const addUser = catchAsync(async (req: Request, res: Response) => {
|
||||
const body: IAddUser = req.body;
|
||||
console.log('body: ', body);
|
||||
const newUser: ICreateUser = {
|
||||
...body,
|
||||
public_key: '',
|
||||
role: 'user',
|
||||
username: generateUsername(),
|
||||
};
|
||||
|
||||
res.status(httpStatus.OK).send();
|
||||
const user = await userService.createUser(newUser);
|
||||
user.status = userStatus['4_Registered'];
|
||||
user.is_registered = true;
|
||||
user.has_account = true;
|
||||
|
||||
await user.save();
|
||||
|
||||
try {
|
||||
await blockchainService.addUser({
|
||||
...body,
|
||||
...newUser,
|
||||
registrator: config.coopname,
|
||||
referer: body.referer ? body.referer : '',
|
||||
coopname: config.coopname,
|
||||
meta: '',
|
||||
});
|
||||
|
||||
const token = await tokenService.generateInviteToken(user.email);
|
||||
await emailService.sendInviteEmail(req.body.email, token);
|
||||
} catch (e: any) {
|
||||
logger.warn('error on add user: ', e);
|
||||
await userService.deleteUserByUsername(newUser.username);
|
||||
throw new ApiError(httpStatus.BAD_GATEWAY, e.message);
|
||||
}
|
||||
|
||||
res.status(httpStatus.CREATED).send({ user });
|
||||
});
|
||||
|
||||
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; //
|
||||
|
||||
//TODO wrong format answer
|
||||
const data = [] as any;
|
||||
for await (let user of users.results) {
|
||||
const json = user.toJSON();
|
||||
json.private_data = (await user.getPrivateData()) || {};
|
||||
data.push(json);
|
||||
}
|
||||
|
||||
result.results = data;
|
||||
result.limit = users.limit;
|
||||
result.page = users.page;
|
||||
|
||||
res.send(result);
|
||||
res.send(users);
|
||||
});
|
||||
|
||||
export const getUser = catchAsync(async (req, res) => {
|
||||
const user = await userService.getUserByUsername(req.params.username);
|
||||
if (!user) {
|
||||
throw new ApiError(http.NOT_FOUND, 'Пользователь не найден');
|
||||
}
|
||||
const json = user.toJSON();
|
||||
|
||||
json.private_data = await user.getPrivateData();
|
||||
|
||||
res.send(json);
|
||||
res.send(user);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
export const deleteUser = catchAsync(async (req, res) => {
|
||||
await userService.deleteUserById(req.params.username);
|
||||
await userService.deleteUserByUsername(req.params.username);
|
||||
res.status(http.NO_CONTENT).send();
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@ import { io, Socket } from 'socket.io-client';
|
||||
import { IAction } from '../types/common';
|
||||
import { SovietContract } from 'cooptypes';
|
||||
import { wsService } from '../services';
|
||||
import logger from '../config/logger';
|
||||
import config from '../config/config';
|
||||
|
||||
let clientSocket: Socket | undefined;
|
||||
|
||||
@@ -10,7 +12,7 @@ export const initSocketConnection = (serverUrl: string): void => {
|
||||
clientSocket = io(serverUrl);
|
||||
|
||||
clientSocket.on('connect', () => {
|
||||
console.log('Успешное подключение к серверу оповещений.');
|
||||
logger.info('Success connection to notification server', { source: 'initSocketConnection' });
|
||||
});
|
||||
|
||||
clientSocket.on('event', (event: IAction) => {
|
||||
@@ -18,7 +20,7 @@ export const initSocketConnection = (serverUrl: string): void => {
|
||||
});
|
||||
|
||||
clientSocket.on('connect_error', (error: Error) => {
|
||||
console.error('Ошибка подключения к серверу оповещений:');
|
||||
logger.error('Fail connect to notification server', { source: 'initSocketConnection' });
|
||||
});
|
||||
};
|
||||
|
||||
@@ -26,13 +28,19 @@ export const initSocketConnection = (serverUrl: string): void => {
|
||||
export const closeSocketConnection = (): void => {
|
||||
if (clientSocket) {
|
||||
clientSocket.disconnect();
|
||||
console.log('Client disconnected from server');
|
||||
logger.info('Client disconnected from server', { source: 'closeSocketConnection' });
|
||||
clientSocket = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
async function processEvent(event: IAction) {
|
||||
if (event.receiver === SovietContract.contractName.production && event.name === 'updateboard') {
|
||||
wsService.updateBoard(event.data);
|
||||
if (event.receiver === config.coopname) {
|
||||
if (event.name === 'updateboard') {
|
||||
wsService.updateBoard(event.data);
|
||||
}
|
||||
|
||||
if (event.name === 'createboard') {
|
||||
wsService.updateBoard(event.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,73 +14,6 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/auth/register": {
|
||||
"post": {
|
||||
"summary": "Register as user",
|
||||
"tags": [
|
||||
"Auth"
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"email",
|
||||
"password"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"description": "must be unique"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"format": "password",
|
||||
"minLength": 8,
|
||||
"description": "At least one number and one letter"
|
||||
}
|
||||
},
|
||||
"example": {
|
||||
"name": "fake name",
|
||||
"email": "fake@example.com",
|
||||
"password": "password1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user": {
|
||||
"$ref": "#/components/schemas/User"
|
||||
},
|
||||
"tokens": {
|
||||
"$ref": "#/components/schemas/AuthTokens"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/DuplicateEmail"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/login": {
|
||||
"post": {
|
||||
"summary": "Login",
|
||||
@@ -844,11 +777,11 @@
|
||||
"ICreatedPayment": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"order_id",
|
||||
"order_num",
|
||||
"confirmation_token"
|
||||
],
|
||||
"properties": {
|
||||
"order_id": {
|
||||
"order_num": {
|
||||
"type": "number"
|
||||
},
|
||||
"confirmation_token": {
|
||||
@@ -1290,7 +1223,7 @@
|
||||
"tags": [
|
||||
{
|
||||
"name": "Auth",
|
||||
"description": "Authentication"
|
||||
"description": "Управление аудентификацией"
|
||||
},
|
||||
{
|
||||
"name": "Orders",
|
||||
|
||||
@@ -3,7 +3,7 @@ import config from '../config/config';
|
||||
const swaggerDef = {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
title: "API Цифрового Кооператива",
|
||||
title: 'API Цифрового Кооператива',
|
||||
// description: `Модуль предоставляет:
|
||||
|
||||
// - API для хранения персональных данных пользователей
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import { spawn, Thread, Worker } from 'threads';
|
||||
import { PluginConfig } from '../models/pluginConfig.model';
|
||||
|
||||
// Объект для хранения запущенных воркеров
|
||||
const workerMap: { [key: string]: { worker: any; thread: Worker } } = {};
|
||||
|
||||
export const initializeDefaultPlugins = async () => {
|
||||
const defaultPlugins = [
|
||||
{
|
||||
name: 'powerup',
|
||||
enabled: true,
|
||||
config: {
|
||||
dailyPackageSize: 5, // Размер ежедневного пакета в AXON
|
||||
topUpAmount: 5, // Сумма пополнения при достижении порога (в AXON)
|
||||
systemSymbol: 'AXON',
|
||||
systemPrecision: 4,
|
||||
thresholds: {
|
||||
cpu: 5000, // Порог CPU в микросекундах
|
||||
net: 1024, // Порог NET в байтах
|
||||
ram: 10240, // Порог RAM в байтах
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const plugin of defaultPlugins) {
|
||||
const existingPlugin = await PluginConfig.findOne({ name: plugin.name });
|
||||
if (!existingPlugin) {
|
||||
await PluginConfig.create(plugin);
|
||||
console.log(`Дефолтный плагин ${plugin.name} добавлен в базу.`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const pluginFactory = async () => {
|
||||
const plugins = await PluginConfig.find({ enabled: true });
|
||||
|
||||
for (const pluginData of plugins) {
|
||||
await initializeWorkerByName(pluginData.name);
|
||||
}
|
||||
};
|
||||
|
||||
// Функция для завершения воркера по имени плагина
|
||||
export const terminateWorker = async (pluginName: string) => {
|
||||
const workerData = workerMap[pluginName];
|
||||
if (workerData) {
|
||||
await workerData.thread.terminate();
|
||||
delete workerMap[pluginName]; // Удаляем воркер из карты
|
||||
console.log(`Воркер для плагина ${pluginName} завершен.`);
|
||||
} else {
|
||||
console.log(`Воркер для плагина ${pluginName} не найден.`);
|
||||
}
|
||||
};
|
||||
|
||||
// Функция для инициализации воркера по имени плагина
|
||||
export const initializeWorkerByName = async (pluginName: string) => {
|
||||
// Проверяем, запущен ли уже воркер
|
||||
if (workerMap[pluginName]) {
|
||||
console.log(`Воркер для плагина ${pluginName} уже запущен.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Получаем данные плагина из базы
|
||||
const pluginData = await PluginConfig.findOne({ name: pluginName });
|
||||
if (!pluginData || !pluginData.enabled) {
|
||||
console.log(`Плагин ${pluginName} не найден или отключен.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Запускаем новый воркер
|
||||
const thread = new Worker('../workers/plugin.worker.ts');
|
||||
const worker = spawn(thread, { timeout: 30000 }); // Не используем await, чтобы не блокировать основной поток
|
||||
//таймаут выше для локальных машин
|
||||
|
||||
// Сохраняем воркера и поток в объекте по имени плагина
|
||||
workerMap[pluginName] = { worker, thread };
|
||||
|
||||
// Воркеры работают в фоне
|
||||
worker.then(async (workerInstance) => {
|
||||
try {
|
||||
const result = await workerInstance.initializePlugin(pluginName);
|
||||
console.log(result); // Плагин инициализирован успешно
|
||||
} catch (err) {
|
||||
console.error(err); // Обработка ошибки
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Воркер для плагина ${pluginName} успешно запущен.`);
|
||||
};
|
||||
|
||||
// Функция для перезагрузки воркера по имени плагина
|
||||
export const restartWorker = async (pluginName: string) => {
|
||||
const workerData = workerMap[pluginName];
|
||||
|
||||
if (workerData) {
|
||||
// Если воркер уже запущен, сначала завершаем его
|
||||
console.log(`Перезагрузка воркера для плагина ${pluginName}...`);
|
||||
await terminateWorker(pluginName);
|
||||
}
|
||||
|
||||
// Затем запускаем воркер заново
|
||||
await initializeWorkerByName(pluginName);
|
||||
console.log(`Воркер для плагина ${pluginName} перезагружен.`);
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
export * from './types';
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
import app from './app';
|
||||
import config from './config/config';
|
||||
import logger from './config/logger';
|
||||
import { connectGenerator } from './services/document.service';
|
||||
import { initPolling } from './services/polling.service';
|
||||
import { redisSubscriber } from './services/redis.service';
|
||||
|
||||
let server: any;
|
||||
|
||||
mongoose.connect(config.mongoose.url).then(async () => {
|
||||
logger.info('Connected to MongoDB with payment processor');
|
||||
|
||||
// подключаемся к хранилищу приватных данных
|
||||
await connectGenerator();
|
||||
|
||||
redisSubscriber.subscribe(`${config.coopname}:orderStatusUpdate`, (err, count) => {
|
||||
if (err) {
|
||||
logger.error('Failed to subscribe: ', err);
|
||||
} else {
|
||||
logger.info(`Subscribed successfully! This client is currently subscribed to ${count} channels.`);
|
||||
}
|
||||
});
|
||||
|
||||
redisSubscriber.on('message', (channel, message) => {
|
||||
if (channel === `${config.coopname}:orderStatusUpdate` && message) {
|
||||
const { orderId, status } = JSON.parse(message);
|
||||
// Дальнейшая обработка
|
||||
}
|
||||
});
|
||||
|
||||
initPolling();
|
||||
});
|
||||
|
||||
const exitHandler = () => {
|
||||
if (server) {
|
||||
server.close(() => {
|
||||
logger.info('Server closed');
|
||||
process.exit(1);
|
||||
});
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
const unexpectedErrorHandler = (error: any) => {
|
||||
logger.error(error);
|
||||
exitHandler();
|
||||
};
|
||||
|
||||
process.on('uncaughtException', unexpectedErrorHandler);
|
||||
process.on('unhandledRejection', unexpectedErrorHandler);
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
logger.info('SIGTERM received');
|
||||
if (server) {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
@@ -1,30 +1,42 @@
|
||||
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, diconnectGenerator } from './services/document.service';
|
||||
import { initSocketConnection } from './controllers/ws.controller';
|
||||
import { initializeDefaultPlugins, pluginFactory } from './factories/pluginFactory';
|
||||
import { paymentService } 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 connectGenerator();
|
||||
|
||||
//подключаемся к ws-серверу
|
||||
initSocketConnection(SERVER_URL);
|
||||
// подключаемся к ws-серверу
|
||||
await initSocketConnection(SERVER_URL);
|
||||
|
||||
// Запуск фабрики платежей
|
||||
await paymentService.init();
|
||||
|
||||
// Инициализация дефолтных плагинов
|
||||
await initializeDefaultPlugins();
|
||||
|
||||
// // Запуск плагинов
|
||||
await pluginFactory();
|
||||
|
||||
server = app.listen(config.port, () => {
|
||||
logger.info(`Listening to port ${config.port}`);
|
||||
});
|
||||
});
|
||||
|
||||
const exitHandler = () => {
|
||||
const exitHandler = async () => {
|
||||
if (server) {
|
||||
server.close(() => {
|
||||
logger.info('Server closed');
|
||||
@@ -35,16 +47,20 @@ const exitHandler = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const unexpectedErrorHandler = (error: any) => {
|
||||
logger.error(error);
|
||||
exitHandler();
|
||||
const unexpectedErrorHandler = async (error: any) => {
|
||||
console.log(error);
|
||||
await diconnectGenerator();
|
||||
await mongoose.disconnect();
|
||||
logger.error(error, { source: 'unexpectedErrorHandler' });
|
||||
await exitHandler();
|
||||
};
|
||||
|
||||
process.on('uncaughtException', unexpectedErrorHandler);
|
||||
process.on('unhandledRejection', unexpectedErrorHandler);
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
process.on('SIGTERM', async () => {
|
||||
logger.info('SIGTERM received');
|
||||
await mongoose.disconnect();
|
||||
if (server) {
|
||||
server.close();
|
||||
}
|
||||
|
||||
@@ -6,20 +6,31 @@ import { roleRights } from '../config/roles';
|
||||
const { UNAUTHORIZED, FORBIDDEN } = httpStatus;
|
||||
|
||||
const verifyCallback = (req, resolve, reject, requiredRights) => async (err, user, info) => {
|
||||
const serverSecret = process.env.SERVER_SECRET;
|
||||
|
||||
// Check for server-secret header
|
||||
if (serverSecret && req.headers['server-secret'] === serverSecret) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
if (err || info || !user) {
|
||||
return reject(new ApiError(UNAUTHORIZED, 'Please authenticate'));
|
||||
}
|
||||
|
||||
req.user = user;
|
||||
|
||||
if (requiredRights.length) {
|
||||
const userRights = roleRights.get(user.role);
|
||||
const hasRequiredRights = requiredRights.every((requiredRight) => userRights?.includes(requiredRight));
|
||||
const userRights = roleRights.get(user.role) || [];
|
||||
|
||||
// if (process.env.IS_PRODUCTION) {
|
||||
if (!hasRequiredRights && req.params.username !== user.username) {
|
||||
return reject(new ApiError(FORBIDDEN, 'Недостаточно прав доступа'));
|
||||
}
|
||||
// }
|
||||
// Determine if user has the required rights
|
||||
const hasRequiredRights = requiredRights.every((requiredRight) => userRights.includes(requiredRight));
|
||||
|
||||
// Check if user is accessing their own resource
|
||||
const isOwnResource =
|
||||
(req.params.username && req.params.username === user.username) ||
|
||||
(req.body.username && req.body.username === user.username);
|
||||
|
||||
if (!isOwnResource && (!hasRequiredRights || !requiredRights.length)) {
|
||||
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;
|
||||
@@ -41,9 +40,7 @@ export const errorHandler = (err, req, res, next) => {
|
||||
...(config.env === 'development' && { stack: err.stack }),
|
||||
};
|
||||
|
||||
if (config.env === 'development') {
|
||||
logger.error(err);
|
||||
}
|
||||
logger.error(err);
|
||||
|
||||
res.status(statusCode).send(response);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import ApiError from '../utils/ApiError';
|
||||
|
||||
const validate = (schema) => (req, res, next) => {
|
||||
// Фильтрация ключей, чтобы убедиться, что они существуют и не пусты
|
||||
const validKeys = ['params', 'query', 'body'].filter(key => req[key] && Object.keys(req[key]).length > 0);
|
||||
const validKeys = ['params', 'query', 'body'].filter((key) => req[key] && Object.keys(req[key]).length > 0);
|
||||
const object = pick(req, validKeys);
|
||||
|
||||
const { value, error } = joi
|
||||
@@ -21,5 +21,4 @@ const validate = (schema) => (req, res, next) => {
|
||||
return next();
|
||||
};
|
||||
|
||||
|
||||
export default validate;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
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';
|
||||
export * from './settings.model';
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import mongoose, { Schema, Document, model } from 'mongoose';
|
||||
import { toJSON, paginate } from './plugins/index';
|
||||
|
||||
export interface IIpn {
|
||||
provider: string;
|
||||
data: object;
|
||||
}
|
||||
|
||||
const ipnSchema = new Schema<IIpn>(
|
||||
{
|
||||
provider: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
// add plugin that converts mongoose to json
|
||||
ipnSchema.plugin(toJSON);
|
||||
ipnSchema.plugin(paginate);
|
||||
|
||||
/**
|
||||
* @typedef Ipn
|
||||
*/
|
||||
const IPN = model<IIpn>('Ipn', ipnSchema);
|
||||
|
||||
export default IPN;
|
||||
@@ -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;
|
||||
@@ -1,34 +1,50 @@
|
||||
import mongoose, { Schema, Document, model } from 'mongoose';
|
||||
import mongoose, { Schema, Document, model, type Model } from 'mongoose';
|
||||
import { toJSON, paginate } from './plugins/index';
|
||||
import AutoIncrementFactory from 'mongoose-sequence';
|
||||
import { generateOrderSecret } from '../services/order.service';
|
||||
import { type IOrder, orderStatus } from '../types/order.types';
|
||||
|
||||
interface IOrder extends Document {
|
||||
creator: string;
|
||||
type: string;
|
||||
data: {
|
||||
username: string
|
||||
provider: string
|
||||
quantity: string
|
||||
};
|
||||
order_id?: number;
|
||||
payed?: boolean;
|
||||
delivered?: boolean;
|
||||
error?: object;
|
||||
expired_at?: Date;
|
||||
const AutoIncrement = AutoIncrementFactory(mongoose);
|
||||
|
||||
interface IOrderModel extends Model<IOrder> {
|
||||
isEmailTaken(email: string, excludeUsername?: mongoose.Types.ObjectId): Promise<boolean>;
|
||||
paginate(filter, options): any;
|
||||
}
|
||||
|
||||
const orderSchema = new Schema<IOrder>({
|
||||
creator: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data: {
|
||||
const orderSchema = new Schema<IOrder, IOrderModel>(
|
||||
{
|
||||
order_num: {
|
||||
type: Number,
|
||||
required: false,
|
||||
unique: true,
|
||||
},
|
||||
creator: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
secret: {
|
||||
type: String,
|
||||
required: true,
|
||||
private: true,
|
||||
default: () => generateOrderSecret(16), // Генерация секрета по умолчанию
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
enum: Object.values(orderStatus),
|
||||
default: orderStatus.pending,
|
||||
},
|
||||
message: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: ['registration', 'deposit'],
|
||||
required: true,
|
||||
},
|
||||
provider: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
username: {
|
||||
type: String,
|
||||
@@ -36,47 +52,66 @@ const orderSchema = new Schema<IOrder>({
|
||||
},
|
||||
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;
|
||||
required: true,
|
||||
},
|
||||
symbol: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
details: {
|
||||
type: {
|
||||
data: { type: Schema.Types.Mixed, required: true },
|
||||
amount_plus_fee: { type: String, required: true },
|
||||
amount_without_fee: { type: String, required: true },
|
||||
fee_amount: { type: String, required: true },
|
||||
fee_percent: { type: Number, required: true },
|
||||
fact_fee_percent: { type: Number, required: true },
|
||||
tolerance_percent: { type: Number, required: true },
|
||||
},
|
||||
required: false, // Поле может быть не обязательным
|
||||
},
|
||||
expired_at: {
|
||||
type: Date,
|
||||
default: () => {
|
||||
const now = new Date();
|
||||
now.setDate(now.getDate() + 1); // Увеличиваем на одни сутки
|
||||
return now;
|
||||
},
|
||||
},
|
||||
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, // Связь с пользователем
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
// Хук после find для автоматического добавления private_data
|
||||
orderSchema.post('find', async function (docs) {
|
||||
for (const doc of docs) {
|
||||
// Наполняем пользователя с помощью populate
|
||||
await doc.populate('user');
|
||||
// Если пользователь найден, вызываем асинхронный метод getPrivateData
|
||||
if (doc.user) {
|
||||
doc.user.private_data = await doc.user.getPrivateData();
|
||||
}
|
||||
|
||||
// Проверяем поле expired_at и обновляем статус
|
||||
if (doc.status != orderStatus.expired && doc.expired_at && new Date() > doc.expired_at) {
|
||||
doc.status = orderStatus.expired;
|
||||
await doc.save(); // Сохраняем изменения в документе
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// add plugin that converts mongoose to json
|
||||
orderSchema.plugin(toJSON);
|
||||
orderSchema.plugin(paginate);
|
||||
// Добавляем автоинкремент для поля order_num
|
||||
orderSchema.plugin(AutoIncrement, { inc_field: 'order_num' });
|
||||
|
||||
/**
|
||||
* @typedef Order
|
||||
*/
|
||||
const Order = model<IOrder>('Order', orderSchema);
|
||||
const Order = model<IOrder, IOrderModel>('Order', orderSchema);
|
||||
|
||||
export default Order;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// models/PaymentState.ts
|
||||
|
||||
import { Schema, model } from 'mongoose';
|
||||
|
||||
interface IPaymentState {
|
||||
accountNumber: string;
|
||||
statementDate: string;
|
||||
lastProcessedPage: number;
|
||||
}
|
||||
|
||||
const PaymentStateSchema = new Schema<IPaymentState>({
|
||||
accountNumber: { type: String, required: true },
|
||||
statementDate: { type: String, required: true },
|
||||
lastProcessedPage: { type: Number, required: true },
|
||||
});
|
||||
|
||||
export const PaymentState = model<IPaymentState>('PaymentState', PaymentStateSchema);
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Schema, model, type Model } from 'mongoose';
|
||||
import type { IPluginSchema } from '../types/plugin.types';
|
||||
import { paginate, toJSON } from './plugins';
|
||||
|
||||
interface IPaginate extends Model<IPluginSchema<any>> {
|
||||
paginate(filter, options): any;
|
||||
}
|
||||
|
||||
export const pluginSchema = new Schema<IPluginSchema<any>, IPaginate>(
|
||||
{
|
||||
name: { type: String, required: true },
|
||||
enabled: { type: Boolean, default: true },
|
||||
config: { type: Schema.Types.Mixed }, // Своя конфигурация для каждого плагина
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
// add plugin that converts mongoose to json
|
||||
pluginSchema.plugin(toJSON);
|
||||
pluginSchema.plugin(paginate);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const PluginConfig = model<IPluginSchema<any>, IPaginate>('Plugins', pluginSchema);
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Schema, model, type Model } from 'mongoose';
|
||||
import type { IPluginLog } from '../types/plugin.types';
|
||||
import { paginate, toJSON } from './plugins';
|
||||
|
||||
interface IPaginate extends Model<IPluginLog<any>> {
|
||||
paginate(filter, options): any;
|
||||
}
|
||||
|
||||
export const pluginLog = new Schema<IPluginLog<any>, IPaginate>(
|
||||
{
|
||||
name: { type: String, required: true },
|
||||
log: { type: Schema.Types.Mixed }, // Своя конфигурация для каждого плагина
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
// add plugin that converts mongoose to json
|
||||
pluginLog.plugin(toJSON);
|
||||
pluginLog.plugin(paginate);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const PluginLog = model<IPluginLog<any>, IPaginate>('PluginLogs', pluginLog);
|
||||
@@ -1,5 +1,7 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
const paginate = (schema) => {
|
||||
/**
|
||||
* @typedef {Object} QueryResult
|
||||
@@ -20,6 +22,11 @@ const paginate = (schema) => {
|
||||
* @returns {Promise<QueryResult>}
|
||||
*/
|
||||
schema.statics.paginate = async function (filter, options) {
|
||||
if (filter.id) {
|
||||
filter._id = new mongoose.Types.ObjectId(String(filter.id));
|
||||
delete filter.id;
|
||||
}
|
||||
|
||||
let sort = '';
|
||||
if (options.sortBy) {
|
||||
const sortingCriteria: (string | never)[] = []; // Update the type of sortingCriteria array
|
||||
@@ -29,7 +36,7 @@ const paginate = (schema) => {
|
||||
});
|
||||
sort = sortingCriteria.join(' ');
|
||||
} else {
|
||||
sort = 'createdAt';
|
||||
sort = '-createdAt';
|
||||
}
|
||||
|
||||
const limit = options.limit && parseInt(options.limit, 10) > 0 ? parseInt(options.limit, 10) : 10;
|
||||
|
||||
@@ -28,11 +28,11 @@ const toJSON = (schema) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ret.id = ret._id.toString();
|
||||
ret.id = ret._id.toString();
|
||||
delete ret._id;
|
||||
delete ret.__v;
|
||||
delete ret.createdAt;
|
||||
delete ret.updatedAt;
|
||||
// delete ret.createdAt;
|
||||
// delete ret.updatedAt;
|
||||
if (transform) {
|
||||
return transform(doc, ret, options);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import mongoose, { Schema, Document, model, type Model } from 'mongoose';
|
||||
import { toJSON, paginate } from './plugins/index';
|
||||
import _ from 'lodash';
|
||||
import type { ISettings } from '../types';
|
||||
|
||||
export interface ISettingsModel extends Model<ISettings> {
|
||||
updateSettings(newSettings: Partial<ISettings>): Promise<ISettings>;
|
||||
getSettings(): Promise<ISettings>;
|
||||
}
|
||||
|
||||
// Определение схемы
|
||||
const settingsSchema = new Schema<ISettings>(
|
||||
{
|
||||
provider: {
|
||||
name: { type: String, required: true, default: 'sberbank' },
|
||||
client: { type: String, default: '' },
|
||||
secret: { type: String, default: '' },
|
||||
},
|
||||
app: {
|
||||
title: { type: String, required: true, default: 'Цифровой Кооператив' },
|
||||
description: { type: String, required: true, default: 'Система электронного документооборота для кооперативов' },
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
// Подключаем плагины
|
||||
settingsSchema.plugin(toJSON);
|
||||
settingsSchema.plugin(paginate);
|
||||
|
||||
// Статические методы
|
||||
settingsSchema.statics.updateSettings = async function (newSettings: Partial<ISettings>): Promise<ISettings> {
|
||||
let settings = await this.findOne();
|
||||
if (!settings) {
|
||||
settings = new this();
|
||||
}
|
||||
|
||||
// Универсальное обновление всех полей
|
||||
_.merge(settings, newSettings);
|
||||
return settings.save();
|
||||
};
|
||||
|
||||
settingsSchema.statics.getSettings = async function (): Promise<ISettings> {
|
||||
let settings = await this.findOne();
|
||||
if (!settings) {
|
||||
settings = new this();
|
||||
await settings.save();
|
||||
}
|
||||
return settings;
|
||||
};
|
||||
|
||||
// Модель
|
||||
const Settings = model<ISettings, ISettingsModel>('Settings', settingsSchema);
|
||||
|
||||
export default Settings;
|
||||
@@ -0,0 +1,51 @@
|
||||
import mongoose, { Schema } from 'mongoose';
|
||||
import { paginate, toJSON } from './plugins';
|
||||
|
||||
export enum tempdocType {
|
||||
JoinStatement = 'joinStatement',
|
||||
WalletAgreement = 'walletAgreement',
|
||||
SignatureAgreement = 'signatureAgreement',
|
||||
PrivacyAgreement = 'privacyAgreement',
|
||||
UserAgreement = 'userAgreement',
|
||||
}
|
||||
|
||||
export interface ITempDocument {
|
||||
username: string;
|
||||
type: tempdocType;
|
||||
document: {
|
||||
hash: string;
|
||||
meta: object;
|
||||
public_key: string;
|
||||
signature: string;
|
||||
};
|
||||
}
|
||||
|
||||
const MonoSchema = new Schema<ITempDocument>({
|
||||
username: { type: String, required: true },
|
||||
type: { type: String, required: true, enum: ['joinStatement', 'walletAgreement'] },
|
||||
document: {
|
||||
public_key: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
signature: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
meta: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
hash: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
MonoSchema.plugin(toJSON);
|
||||
MonoSchema.plugin(paginate);
|
||||
|
||||
const TempDocument = mongoose.model<ITempDocument>('TempDocument', MonoSchema);
|
||||
|
||||
export default TempDocument;
|
||||
@@ -25,7 +25,7 @@ const tokenSchema = new Schema<IToken>(
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: [tokenTypes.REFRESH, tokenTypes.RESET_PASSWORD, tokenTypes.VERIFY_EMAIL],
|
||||
enum: [tokenTypes.REFRESH, tokenTypes.RESET_PASSWORD, tokenTypes.VERIFY_EMAIL, tokenTypes.INVITE],
|
||||
required: true,
|
||||
},
|
||||
expires: {
|
||||
@@ -39,6 +39,7 @@ const tokenSchema = new Schema<IToken>(
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
toJSON: { virtuals: true },
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -1,42 +1,12 @@
|
||||
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';
|
||||
import { userStatus, type IUser } from '../types/user.types';
|
||||
|
||||
const { isEmail } = validator;
|
||||
const { compare, hash } = bcryptjs;
|
||||
|
||||
export interface IUser extends Document {
|
||||
username: string;
|
||||
status: 'created' | 'joined' | 'payed' | 'registered' | 'active' | 'failed' | 'blocked';
|
||||
message: string;
|
||||
is_registered: boolean;
|
||||
type: 'individual' | 'entrepreneur' | 'organization';
|
||||
public_key: string;
|
||||
referer: string;
|
||||
email: string;
|
||||
password: string;
|
||||
role: string;
|
||||
is_email_verified: boolean;
|
||||
statement: {
|
||||
hash: string;
|
||||
meta: any;
|
||||
public_key: string;
|
||||
signature: string;
|
||||
};
|
||||
private_data:
|
||||
| Cooperative.Users.IIndividualData
|
||||
| Cooperative.Users.IEntrepreneurData
|
||||
| Cooperative.Users.IOrganizationData
|
||||
| null;
|
||||
getPrivateData(): Promise<
|
||||
Cooperative.Users.IIndividualData | Cooperative.Users.IEntrepreneurData | Cooperative.Users.IOrganizationData | null
|
||||
>;
|
||||
isPasswordMatch(password: string): Promise<boolean>;
|
||||
}
|
||||
|
||||
interface IUserModel extends Model<IUser> {
|
||||
isEmailTaken(email: string, excludeUsername?: mongoose.Types.ObjectId): Promise<boolean>;
|
||||
@@ -53,8 +23,8 @@ const userSchema = new Schema<IUser, IUserModel>(
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
enum: ['created', 'joined', 'payed', 'registered', 'active', 'failed', 'blocked'],
|
||||
default: 'created',
|
||||
enum: Object.values(userStatus),
|
||||
default: userStatus['1_Created'],
|
||||
},
|
||||
message: {
|
||||
type: String,
|
||||
@@ -71,7 +41,7 @@ const userSchema = new Schema<IUser, IUserModel>(
|
||||
},
|
||||
public_key: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '',
|
||||
},
|
||||
referer: {
|
||||
type: String,
|
||||
@@ -79,7 +49,7 @@ const userSchema = new Schema<IUser, IUserModel>(
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
required: false,
|
||||
unique: true,
|
||||
trim: true,
|
||||
lowercase: true,
|
||||
@@ -89,18 +59,6 @@ const userSchema = new Schema<IUser, IUserModel>(
|
||||
}
|
||||
},
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true,
|
||||
trim: true,
|
||||
minlength: 8,
|
||||
validate(value) {
|
||||
if (!value.match(/\d/) || !value.match(/[a-zA-Z]/)) {
|
||||
throw new Error('Password must contain at least one letter and one number');
|
||||
}
|
||||
},
|
||||
private: true, // used by the toJSON plugin
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
enum: roles,
|
||||
@@ -114,26 +72,13 @@ const userSchema = new Schema<IUser, IUserModel>(
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
statement: {
|
||||
public_key: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
signature: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
meta: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
hash: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
initial_order: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Order',
|
||||
},
|
||||
},
|
||||
{
|
||||
minimize: false,
|
||||
timestamps: true,
|
||||
toJSON: { virtuals: true },
|
||||
}
|
||||
@@ -149,25 +94,44 @@ userSchema.statics.isEmailTaken = async function (email) {
|
||||
return !!user;
|
||||
};
|
||||
|
||||
// Хук после find для автоматического добавления private_data
|
||||
userSchema.post('find', async function (docs) {
|
||||
for (const doc of docs) {
|
||||
// Наполняем пользователя с помощью populate
|
||||
doc.private_data = await doc.getPrivateData();
|
||||
}
|
||||
});
|
||||
|
||||
// Хук после findOne для автоматического добавления private_data
|
||||
userSchema.post('findOne', async function (doc) {
|
||||
if (doc) {
|
||||
doc.private_data = await doc.getPrivateData();
|
||||
}
|
||||
});
|
||||
|
||||
// Виртуальное свойство для private_data
|
||||
userSchema
|
||||
.virtual('private_data')
|
||||
.get(function () {
|
||||
return this._privateData; // Чтение из временного поля
|
||||
})
|
||||
.set(function (value) {
|
||||
this._privateData = value; // Установка временного поля
|
||||
});
|
||||
|
||||
// Асинхронный метод получения private_data
|
||||
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 privateData = (await generator.get(this.type, { username: this.username })) as
|
||||
| Cooperative.Users.IIndividualData
|
||||
| Cooperative.Users.IEntrepreneurData
|
||||
| Cooperative.Users.IOrganizationData
|
||||
| null;
|
||||
this.private_data = privateData; // Устанавливаем виртуальное свойство
|
||||
return privateData;
|
||||
};
|
||||
|
||||
userSchema.methods.isPasswordMatch = async function (password) {
|
||||
const user = this;
|
||||
return compare(password, user.password);
|
||||
};
|
||||
|
||||
userSchema.pre('save', async function (next) {
|
||||
const user = this;
|
||||
if (user.isModified('password')) {
|
||||
user.password = await hash(user.password, 8);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
const User = model<IUser, IUserModel>('User', userSchema);
|
||||
|
||||
export default User;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import mongoose, { Schema, type Model } from 'mongoose';
|
||||
import { paginate, toJSON } from './plugins';
|
||||
import { decrypt, encrypt } from '../utils/aes';
|
||||
|
||||
export interface IVault {
|
||||
username: string;
|
||||
permission: wifPermissions;
|
||||
wif: string;
|
||||
}
|
||||
|
||||
export enum wifPermissions {
|
||||
'Active' = 'active',
|
||||
}
|
||||
|
||||
interface IVaultActions extends Model<IVault> {
|
||||
getWif(username: string, permission?: wifPermissions): Promise<string | null>;
|
||||
setWif(username: string, wif: string, permission?: wifPermissions): Promise<boolean>;
|
||||
}
|
||||
|
||||
const VaultSchema = new Schema<IVault, IVaultActions>({
|
||||
username: { type: String, required: true },
|
||||
permission: { type: String, required: true, enum: Object.values(wifPermissions) },
|
||||
wif: { type: String, required: true, private: true },
|
||||
});
|
||||
|
||||
// Метод для извлечения WIF с дефолтным значением permission
|
||||
VaultSchema.statics.getWif = async function (
|
||||
username: string,
|
||||
permission: wifPermissions = wifPermissions.Active
|
||||
): Promise<string | null> {
|
||||
const vault = await this.findOne({ username, permission });
|
||||
if (vault) {
|
||||
try {
|
||||
return decrypt(vault.wif); // Расшифровываем WIF перед возвратом
|
||||
} catch (error) {
|
||||
console.error('Ошибка при расшифровке WIF:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Метод для обновления или создания WIF с дефолтным значением permission
|
||||
VaultSchema.statics.setWif = async function (
|
||||
username: string,
|
||||
wif: string,
|
||||
permission: wifPermissions = wifPermissions.Active
|
||||
): Promise<boolean> {
|
||||
const encryptedWif = encrypt(wif); // Шифруем WIF перед сохранением
|
||||
|
||||
const result = await this.findOneAndUpdate(
|
||||
{ username, permission },
|
||||
{ wif: encryptedWif },
|
||||
{ new: true, upsert: true } // upsert создает новую запись, если не существует
|
||||
);
|
||||
|
||||
return result !== null;
|
||||
};
|
||||
|
||||
VaultSchema.plugin(toJSON);
|
||||
VaultSchema.plugin(paginate);
|
||||
|
||||
const Vault = mongoose.model<IVault, IVaultActions>('Vault', VaultSchema);
|
||||
|
||||
export default Vault;
|
||||
@@ -0,0 +1,5 @@
|
||||
import * as powerup from './powerup/powerup.plugin';
|
||||
|
||||
export const PluginRegistry = {
|
||||
powerup,
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.1.3](https://github.com/coopenomics/monocoop/compare/v2.1.2-alpha.10...v2.1.3) (2024-10-26)
|
||||
|
||||
|
||||
|
||||
## [2.1.2](https://github.com/coopenomics/monocoop/compare/v2.1.1...v2.1.2) (2024-10-19)
|
||||
|
||||
**Note:** Version bump only for package powerup
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.2](https://github.com/coopenomics/monocoop/compare/v2.1.1...v2.1.2) (2024-10-19)
|
||||
|
||||
**Note:** Version bump only for package powerup
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "powerup",
|
||||
"version": "2.1.3",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"gitHead": "7b59ea4b96991ed06abdc92e6d7835632845e3a5"
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
import { IPlugin, type IPluginLog, type IPluginSchema } from '../../types/plugin.types';
|
||||
import Joi from 'joi';
|
||||
import cron from 'node-cron';
|
||||
import { blockchainService } from '../../services';
|
||||
import logger from '../../config/logger';
|
||||
import { default as coopConfig } from '../../config/config';
|
||||
import { PluginConfig } from '../../models/pluginConfig.model';
|
||||
import { PluginLog } from '../../models/pluginLog.model';
|
||||
import type { IGetPluginConfig } from '../../types';
|
||||
import type { Document, Types } from 'mongoose';
|
||||
|
||||
// Интерфейс для параметров конфигурации плагина powerup
|
||||
export interface IConfig {
|
||||
dailyPackageSize: number; // Размер ежедневного пакета в AXON
|
||||
topUpAmount: number; // Сумма пополнения при достижении порога (в AXON)
|
||||
systemSymbol: string;
|
||||
systemPrecision: number;
|
||||
lastDailyReplenishmentDate: Date;
|
||||
thresholds: {
|
||||
cpu: number; // Порог CPU в микросекундах
|
||||
net: number; // Порог NET в байтах
|
||||
ram: number; // Порог RAM в байтах
|
||||
};
|
||||
}
|
||||
|
||||
export interface ILog {
|
||||
type: 'daily' | 'now';
|
||||
amount: string;
|
||||
resources: {
|
||||
username: string;
|
||||
ram_usage: any;
|
||||
ram_quota: any;
|
||||
net_limit: any;
|
||||
cpu_limit: any;
|
||||
};
|
||||
}
|
||||
|
||||
export class Plugin implements IPlugin {
|
||||
name = 'powerup';
|
||||
data!: IPluginSchema<IConfig>;
|
||||
|
||||
public pluginSchema = Joi.object<IConfig>({
|
||||
dailyPackageSize: Joi.number().required(),
|
||||
topUpAmount: Joi.number().required(),
|
||||
thresholds: Joi.object({
|
||||
cpu: Joi.number().required(),
|
||||
net: Joi.number().required(),
|
||||
ram: Joi.number().required(),
|
||||
}).required(),
|
||||
});
|
||||
|
||||
async initialize() {
|
||||
const pluginData = await PluginConfig.findOne({ name: this.name });
|
||||
if (!pluginData) throw new Error('Конфиг не найден');
|
||||
|
||||
this.data = pluginData;
|
||||
|
||||
logger.info(`Инициализация ${this.name} с конфигурацией`, this.data);
|
||||
|
||||
// Проверяем, было ли ежедневное пополнение в последние 24 часа
|
||||
const lastDate = this.data.config.lastDailyReplenishmentDate;
|
||||
const now = new Date();
|
||||
|
||||
if (lastDate) {
|
||||
const diffInHours = Math.abs(now.getTime() - lastDate.getTime()) / 36e5; // Разница во времени в часах
|
||||
if (diffInHours < 24) {
|
||||
logger.info('Ежедневное пополнение уже выполнялось в последние 24 часа. Повторное пополнение не требуется.');
|
||||
} else {
|
||||
logger.info('Ежедневное пополнение не выполнялось в последние 24 часа. Выполняем пополнение...');
|
||||
await this.runDailyTask();
|
||||
}
|
||||
} else {
|
||||
logger.info('Дата последнего пополнения отсутствует. Выполняем пополнение...');
|
||||
await this.runDailyTask();
|
||||
}
|
||||
|
||||
// Регистрация cron-задачи для ежедневного пополнения
|
||||
cron.schedule('0 0 * * *', () => {
|
||||
logger.info('Запуск ежедневной задачи пополнения');
|
||||
this.runDailyTask();
|
||||
});
|
||||
|
||||
// Регистрация cron-задачи для проверки ресурсов каждую минуту
|
||||
cron.schedule('* * * * *', () => {
|
||||
logger.info('Запуск задачи проверки ресурсов');
|
||||
this.runTask();
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
await new Promise(() => {});
|
||||
}
|
||||
|
||||
private getQuantity(amount: number): string {
|
||||
return `${amount.toFixed(this.data.config.systemPrecision)} ${this.data.config.systemSymbol}`;
|
||||
}
|
||||
|
||||
// Ежедневная задача пополнения
|
||||
private async runDailyTask() {
|
||||
const quantity = this.getQuantity(this.data.config.dailyPackageSize);
|
||||
|
||||
logger.info(`Выполнение ежедневного пополнения на сумму ${quantity}`);
|
||||
|
||||
try {
|
||||
// Получаем имя пользователя из окружения или другой конфигурации
|
||||
const username = coopConfig.coopname;
|
||||
const account = await blockchainService.getBlockchainAccount(username);
|
||||
|
||||
await blockchainService.powerUp(username, quantity);
|
||||
|
||||
this.data.config.lastDailyReplenishmentDate = new Date();
|
||||
this.data.markModified('config'); // Помечаем config как измененное поле
|
||||
|
||||
await this.data.save();
|
||||
|
||||
await this.log({
|
||||
type: 'daily',
|
||||
amount: quantity,
|
||||
resources: {
|
||||
username: account.account_name,
|
||||
ram_usage: account.ram_usage,
|
||||
ram_quota: account.ram_quota,
|
||||
net_limit: account.net_limit,
|
||||
cpu_limit: account.cpu_limit,
|
||||
},
|
||||
});
|
||||
|
||||
logger.info('Ежедневное пополнение выполнено успешно');
|
||||
} catch (error) {
|
||||
console.error('Ошибка при выполнении ежедневного пополнения:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private async log(action: ILog) {
|
||||
await PluginLog.create({ name: this.name, log: action });
|
||||
}
|
||||
|
||||
// Задача проверки и пополнения ресурсов
|
||||
private async runTask() {
|
||||
logger.info(`Задача плагина ${this.name} выполняется...`);
|
||||
|
||||
try {
|
||||
// Получаем имя пользователя из окружения или другой конфигурации
|
||||
const username = coopConfig.coopname;
|
||||
|
||||
const account = await blockchainService.getBlockchainAccount(username);
|
||||
|
||||
// Получаем текущие значения квот
|
||||
const cpuLimit = account.cpu_limit;
|
||||
const netLimit = account.net_limit;
|
||||
const ramQuota = account.ram_quota;
|
||||
const ramUsage = account.ram_usage;
|
||||
const availableRam = ramQuota - ramUsage;
|
||||
|
||||
// Проверяем пороги и пополняем при необходимости
|
||||
let needPowerUp = false;
|
||||
|
||||
if (cpuLimit.available <= this.data.config.thresholds.cpu) {
|
||||
logger.info(`CPU квота ниже порога (${cpuLimit.available} µs).`);
|
||||
needPowerUp = true;
|
||||
}
|
||||
|
||||
if (netLimit.available <= this.data.config.thresholds.net) {
|
||||
logger.info(`NET квота ниже порога (${netLimit.available} bytes).`);
|
||||
needPowerUp = true;
|
||||
}
|
||||
|
||||
if (availableRam <= this.data.config.thresholds.ram) {
|
||||
logger.info(`RAM квота ниже порога (${availableRam} bytes).`);
|
||||
needPowerUp = true;
|
||||
}
|
||||
|
||||
if (needPowerUp) {
|
||||
// Выполняем пополнение ресурсов
|
||||
const quantity = this.getQuantity(this.data.config.topUpAmount);
|
||||
await blockchainService.powerUp(username, quantity);
|
||||
logger.info(`Пополнение выполнено на сумму ${quantity}.`);
|
||||
|
||||
await this.log({
|
||||
type: 'now',
|
||||
amount: quantity,
|
||||
resources: {
|
||||
username: account.account_name,
|
||||
ram_usage: account.ram_usage,
|
||||
ram_quota: account.ram_quota,
|
||||
net_limit: account.net_limit,
|
||||
cpu_limit: account.cpu_limit,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
logger.info('Квоты ресурсов в норме, пополнение не требуется.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при проверке и пополнении ресурсов:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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('/send-verification-email', auth(), authController.sendVerificationEmail);
|
||||
router.post('/verify-email', validate(authValidation.verifyEmail), authController.verifyEmail);
|
||||
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('sendVerificationEmail'), authController.sendVerificationEmail);
|
||||
router.post('/verify-email', validate(authValidation.RVerifyEmail), authController.verifyEmail);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -21,55 +21,7 @@ export default router;
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Auth
|
||||
* description: Authentication
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /auth/register:
|
||||
* post:
|
||||
* summary: Register as user
|
||||
* tags: [Auth]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - name
|
||||
* - email
|
||||
* - password
|
||||
* properties:
|
||||
* name:
|
||||
* type: string
|
||||
* email:
|
||||
* type: string
|
||||
* format: email
|
||||
* description: must be unique
|
||||
* password:
|
||||
* type: string
|
||||
* format: password
|
||||
* minLength: 8
|
||||
* description: At least one number and one letter
|
||||
* example:
|
||||
* name: fake name
|
||||
* email: fake@example.com
|
||||
* password: password1
|
||||
* responses:
|
||||
* "201":
|
||||
* description: Created
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* user:
|
||||
* $ref: '#/components/schemas/User'
|
||||
* tokens:
|
||||
* $ref: '#/components/schemas/AuthTokens'
|
||||
* "400":
|
||||
* $ref: '#/components/responses/DuplicateEmail'
|
||||
* description: Управление аудентификацией
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,21 @@
|
||||
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('regenerateDocument'), validate(documentValidation.RGenerate), documentController.generateDocument);
|
||||
|
||||
router
|
||||
.route('/get-documents')
|
||||
.get(auth('getDocuments'), validate(documentValidation.RGetDocuments), documentController.getDocuments);
|
||||
|
||||
// router
|
||||
// .route('/get-my-documents')
|
||||
// .get(auth('getMyDocuments'), validate(documentValidation.RGetDocuments), documentController.getDocuments);
|
||||
|
||||
export default router;
|
||||
@@ -4,14 +4,22 @@ import userRoute from './user.route';
|
||||
import docsRoute from './docs.route';
|
||||
import orderRoute from './order.route';
|
||||
import coopRoute from './coop.route';
|
||||
import dataRoute from './data.route';
|
||||
import monoRoute from './system.route';
|
||||
import dataRoute from './document.route';
|
||||
import notifyRoute from './notify.route';
|
||||
import participantsRoute from './participant.route';
|
||||
import methodRoute from './method.route';
|
||||
import pluginRoute from './plugin.route';
|
||||
|
||||
import config from '../../config/config';
|
||||
|
||||
const router = Router();
|
||||
|
||||
const defaultRoutes = [
|
||||
{
|
||||
path: '/system',
|
||||
route: monoRoute,
|
||||
},
|
||||
{
|
||||
path: '/auth',
|
||||
route: authRoute,
|
||||
@@ -21,9 +29,17 @@ const defaultRoutes = [
|
||||
route: userRoute,
|
||||
},
|
||||
{
|
||||
path: '/data',
|
||||
path: '/participants',
|
||||
route: participantsRoute,
|
||||
},
|
||||
{
|
||||
path: '/documents',
|
||||
route: dataRoute,
|
||||
},
|
||||
{
|
||||
path: '/methods',
|
||||
route: methodRoute,
|
||||
},
|
||||
{
|
||||
path: '/orders',
|
||||
route: orderRoute,
|
||||
@@ -36,6 +52,10 @@ const defaultRoutes = [
|
||||
path: '/notify',
|
||||
route: notifyRoute,
|
||||
},
|
||||
{
|
||||
path: '/plugins',
|
||||
route: pluginRoute,
|
||||
},
|
||||
];
|
||||
|
||||
const devRoutes = [
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Router } from 'express';
|
||||
import { methodController } from '../../controllers';
|
||||
import validate from '../../middlewares/validate';
|
||||
import { methodValidation } from '../../validations';
|
||||
import auth from '../../middlewares/auth';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router
|
||||
.route('/:username?')
|
||||
.get(auth('manageMyMethods'), validate(methodValidation.RGetListPaymentMethods), methodController.listPaymentMethods);
|
||||
|
||||
router
|
||||
.route('/:username/add')
|
||||
.post(auth('manageMyMethods'), validate(methodValidation.RSavePaymentMethod), methodController.addPaymentMethod);
|
||||
|
||||
router
|
||||
.route('/:username/delete')
|
||||
.post(auth('manageMyMethods'), validate(methodValidation.RDeletePaymentMethod), methodController.deletePaymentMethod);
|
||||
|
||||
export default router;
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Router } from 'express';
|
||||
import { orderController } from '../../controllers';
|
||||
import { methodController, orderController } from '../../controllers';
|
||||
import validate from '../../middlewares/validate';
|
||||
import * as orderValidation from '../../validations/order.validation';
|
||||
import { orderValidation } from '../../validations';
|
||||
import auth from '../../middlewares/auth';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -21,10 +20,10 @@ const router = Router();
|
||||
* ICreatedPayment:
|
||||
* type: object
|
||||
* required:
|
||||
* - order_id
|
||||
* - order_num
|
||||
* - confirmation_token
|
||||
* properties:
|
||||
* order_id:
|
||||
* order_num:
|
||||
* type: number
|
||||
* confirmation_token:
|
||||
* type: string
|
||||
@@ -41,7 +40,7 @@ const router = Router();
|
||||
* 200:
|
||||
* description: Успешное выполнение запроса.
|
||||
*/
|
||||
router.route('/ipn').post(validate(orderValidation.RRecieveIPN), orderController.catchIPN);
|
||||
router.route('/:provider/ipn').post(validate(orderValidation.RRecieveIPN), orderController.catchIPN);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@@ -66,7 +65,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('initialPayment'), validate(orderValidation.RCreateInitialPayment), orderController.createInitialPayment);
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@@ -91,6 +92,15 @@ 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('createDeposit'), validate(orderValidation.RCreateDeposit), orderController.createDeposit);
|
||||
|
||||
router
|
||||
.route('/set-order-status')
|
||||
.post(auth('manageOrders'), validate(orderValidation.RSetOrderStatus), orderController.setStatus);
|
||||
|
||||
router.route('/all').get(auth('manageOrders'), validate(orderValidation.RGetCoopOrders), orderController.getOrders);
|
||||
router.route('/:username').get(auth('getMyOrders'), validate(orderValidation.RGetMyOrders), orderController.getMyOrders);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Router } from 'express';
|
||||
import auth from '../../middlewares/auth';
|
||||
import validate from '../../middlewares/validate';
|
||||
import { participantValidation } from '../../validations';
|
||||
import { participantController } from '../../controllers';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router
|
||||
.route('/join-cooperative')
|
||||
.post(auth('joinCooperative'), validate(participantValidation.RJoinCooperative), participantController.joinCooperative);
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Router } from 'express';
|
||||
import { pluginController } from '../../controllers';
|
||||
import auth from '../../middlewares/auth';
|
||||
import validate from '../../middlewares/validate';
|
||||
import { pluginValidation } from '../../validations';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.route('/').post(auth('setPlugin'), validate(pluginValidation.RSetPlugin), pluginController.setPluginConfig);
|
||||
router.route('/').get(auth('setPlugin'), validate(pluginValidation.RGetPluginList), pluginController.getPluginList);
|
||||
|
||||
router
|
||||
.route('/schema')
|
||||
.get(auth('setPlugin'), validate(pluginValidation.RGetPluginSchema), pluginController.getPluginSchema);
|
||||
|
||||
router
|
||||
.route('/config')
|
||||
.get(auth('setPlugin'), validate(pluginValidation.RGetPluginConfig), pluginController.getPluginConfig);
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Router } from 'express';
|
||||
import { settingController, systemController } from '../../controllers';
|
||||
import auth from '../../middlewares/auth';
|
||||
import validate from '../../middlewares/validate';
|
||||
import { systemValidation } from '../../validations';
|
||||
|
||||
const router = Router();
|
||||
|
||||
//init - устанавливает данные организации кооператива
|
||||
router.route('/init').post(auth('init'), validate(systemValidation.RInit), systemController.init);
|
||||
|
||||
router.route('/install').post(validate(systemValidation.RInstall), systemController.install);
|
||||
|
||||
router.route('/wif').post(auth('setWif'), validate(systemValidation.RSetWif), systemController.setWif);
|
||||
|
||||
//wif
|
||||
|
||||
router.route('/vars/schema').get(auth('getVars'), systemController.getVarsSchema);
|
||||
router.route('/vars').get(auth('getVars'), systemController.getVars);
|
||||
router.route('/vars').post(auth('setVars'), validate(systemValidation.RSetVars), systemController.setVars);
|
||||
|
||||
router.route('/health').get(systemController.getHealth);
|
||||
|
||||
router.route('/settings').get(auth('manageSettings'), settingController.getSettings);
|
||||
|
||||
router
|
||||
.route('/settings')
|
||||
.post(auth('manageSettings'), validate(systemValidation.RUpdateSettings), settingController.updateSettings);
|
||||
|
||||
export default router;
|
||||
@@ -11,13 +11,11 @@ router
|
||||
.post(validate(userValidation.RCreateUser), userController.createUser)
|
||||
.get(auth('getUsers'), validate(userValidation.RGetUsers), userController.getUsers);
|
||||
|
||||
router
|
||||
.route('/join-cooperative')
|
||||
.post(validate(userValidation.RJoinCooperative), userController.joinCooperative)
|
||||
router.route('/add').post(auth('addUser'), validate(userValidation.RAddUser), userController.addUser);
|
||||
|
||||
router
|
||||
.route('/:username')
|
||||
.get(auth('getUsers'), validate(userValidation.RGetUser), userController.getUser)
|
||||
.get(auth('getSelf'), validate(userValidation.RGetUser), userController.getUser)
|
||||
.patch(auth('manageUsers'), validate(userValidation.RUpdateUser), userController.updateUser);
|
||||
|
||||
export default router;
|
||||
@@ -200,3 +198,33 @@ export default router;
|
||||
* $ref: '#/components/responses/NotFound'
|
||||
*
|
||||
*/
|
||||
|
||||
// /**
|
||||
// * @swagger
|
||||
// * /users/add:
|
||||
// * post:
|
||||
// * summary: Добавить пользователя без заявления
|
||||
// * description: Только администраторы могут добавлять новых пользователей без прохождения процедуры подачи заявления. Это используется для добавления действующих пайщиков.
|
||||
// * tags: [Users]
|
||||
// * security:
|
||||
// * - bearerAuth: []
|
||||
// * requestBody:
|
||||
// * required: true
|
||||
// * content:
|
||||
// * application/json:
|
||||
// * schema:
|
||||
// * "$ref": '#/components/schemas/IAddUser'
|
||||
// * responses:
|
||||
// * "201":
|
||||
// * description: Пользователь успешно добавлен
|
||||
// * content:
|
||||
// * application/json:
|
||||
// * schema:
|
||||
// * $ref: '#/components/schemas/User'
|
||||
// * "400":
|
||||
// * $ref: '#/components/responses/BadRequest'
|
||||
// * "401":
|
||||
// * $ref: '#/components/responses/Unauthorized'
|
||||
// * "403":
|
||||
// * $ref: '#/components/responses/Forbidden'
|
||||
// */
|
||||
|
||||
@@ -1,67 +1,50 @@
|
||||
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();
|
||||
}
|
||||
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.UNAUTHORIZED, 'Неверный приватный ключ');
|
||||
}
|
||||
} catch (e: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Ошибка при автоматической проверке целевой авторизации: ', e.message);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Login with username and password
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
export const loginUserWithUsernameAndPassword = async (username, password) => {
|
||||
const user = await userService.getUserByUsername(username);
|
||||
if (!user || !(await user.isPasswordMatch(password))) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Incorrect username or password');
|
||||
}
|
||||
return user;
|
||||
};
|
||||
|
||||
@@ -75,7 +58,7 @@ export const logout = async (refreshToken) => {
|
||||
if (!refreshTokenDoc) {
|
||||
throw new ApiError(httpStatus.NOT_FOUND, 'Not found');
|
||||
}
|
||||
await refreshTokenDoc.remove();
|
||||
await refreshTokenDoc.deleteOne();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -83,16 +66,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,22 +81,34 @@ 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 resetKeyTokenDoc = await tokenService.verifyToken(resetKeyToken, [tokenTypes.RESET_PASSWORD, tokenTypes.INVITE]);
|
||||
|
||||
const user = await userService.getUserById(resetKeyTokenDoc.user);
|
||||
if (!user) {
|
||||
throw new Error();
|
||||
}
|
||||
await userService.updateUserById(user.username, { password: newPassword });
|
||||
await Token.deleteMany({ user: user.username, type: tokenTypes.RESET_PASSWORD });
|
||||
|
||||
if (config.env !== 'test')
|
||||
await blockchainService.changeKey({
|
||||
coopname: config.coopname,
|
||||
changer: config.coopname,
|
||||
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) {
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Password reset failed');
|
||||
console.log(error);
|
||||
throw new ApiError(httpStatus.UNAUTHORIZED, 'Token reset failed');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -127,12 +120,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');
|
||||
}
|
||||
|
||||
@@ -1,14 +1,55 @@
|
||||
import { Api, JsonRpc } from 'eosjs';
|
||||
import { JsSignatureProvider } from 'eosjs/dist/eosjs-jssig';
|
||||
import { TextEncoder, TextDecoder } from 'util';
|
||||
import fetch from 'isomorphic-fetch';
|
||||
import EosApi from 'eosjs-api';
|
||||
import getInternalAction from '../utils/getInternalAction';
|
||||
import { GatewayContract, RegistratorContract, SovietContract } from 'cooptypes';
|
||||
import { IUser } from '../models/user.model';
|
||||
import { GatewayContract, RegistratorContract, SovietContract, type SystemContract } from 'cooptypes';
|
||||
import { IUser } from '../types/user.types';
|
||||
import { GetAccountResult, GetInfoResult } from 'eosjs/dist/eosjs-rpc-interfaces';
|
||||
import config from '../config/config';
|
||||
import TempDocument, { tempdocType } from '../models/tempDocument.model';
|
||||
import ApiError from '../utils/ApiError';
|
||||
import httpStatus from 'http-status';
|
||||
import type { IOrder } from '../types/order.types';
|
||||
import type { IBCAction } from '../types';
|
||||
import Vault from '../models/vault.model';
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
export async function transact(actions: any): Promise<any> {
|
||||
const instance = await getInstance(config.coopname);
|
||||
const result = await instance.transact(
|
||||
{
|
||||
actions,
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
}
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить инстанс для осуществления транзакции в блокчейн.
|
||||
* @param {username} - авторизация от аккаунта
|
||||
@@ -16,15 +57,19 @@ const rpc = new JsonRpc(process.env.BLOCKCHAIN_RPC as string, { fetch });
|
||||
* @returns {eosjs-api}
|
||||
*/
|
||||
|
||||
async function getInstance(wif) {
|
||||
async function getInstance(username: string) {
|
||||
const wif = await Vault.getWif(username);
|
||||
|
||||
if (!wif) throw new ApiError(httpStatus.BAD_GATEWAY, 'Не найден приватный ключ для совершения операции');
|
||||
|
||||
const signatureProvider = new JsSignatureProvider([wif]);
|
||||
const api = new Api({ rpc, signatureProvider, textDecoder: new TextDecoder(), textEncoder: new TextEncoder() });
|
||||
const api = new Api({ rpc, signatureProvider });
|
||||
return api;
|
||||
}
|
||||
|
||||
async function getApi() {
|
||||
function getApi() {
|
||||
const options = {
|
||||
httpEndpoint: process.env.BLOCKCHAIN_RPC, // default, null for cold-storage
|
||||
httpEndpoint: process.env.BLOCKCHAIN_RPC, //
|
||||
verbose: false, // API logging
|
||||
fetchConfiguration: {},
|
||||
};
|
||||
@@ -63,9 +108,9 @@ async function getCooperative(coopname) {
|
||||
|
||||
const [cooperative] = await lazyFetch(
|
||||
api,
|
||||
process.env.REGISTRATOR_CONTRACT,
|
||||
process.env.REGISTRATOR_CONTRACT,
|
||||
'orgs',
|
||||
RegistratorContract.contractName.production,
|
||||
RegistratorContract.contractName.production,
|
||||
RegistratorContract.Tables.Cooperatives.tableName,
|
||||
coopname,
|
||||
coopname,
|
||||
1
|
||||
@@ -73,9 +118,11 @@ async function getCooperative(coopname) {
|
||||
return cooperative;
|
||||
}
|
||||
|
||||
async function registerBlockchainAccount(user: IUser, orderData: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit) {
|
||||
const eos = await getInstance(process.env.REGISTRATOR_WIF);
|
||||
async function registerBlockchainAccount(user: IUser, order: IOrder) {
|
||||
const eos = await getInstance(config.coopname);
|
||||
const actions = [] as any;
|
||||
|
||||
// Создаем newaccount
|
||||
const newaccount: RegistratorContract.Actions.CreateAccount.ICreateAccount = {
|
||||
registrator: process.env.COOPNAME as string,
|
||||
coopname: process.env.COOPNAME as string,
|
||||
@@ -85,6 +132,20 @@ async function registerBlockchainAccount(user: IUser, orderData: GatewayContract
|
||||
meta: '',
|
||||
};
|
||||
|
||||
actions.push({
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.CreateAccount.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: newaccount,
|
||||
});
|
||||
|
||||
// Создаем registerUserData
|
||||
|
||||
const registerUserData: RegistratorContract.Actions.RegisterUser.IRegistrerUser = {
|
||||
coopname: process.env.COOPNAME as string,
|
||||
registrator: process.env.COOPNAME as string,
|
||||
@@ -92,61 +153,206 @@ async function registerBlockchainAccount(user: IUser, orderData: GatewayContract
|
||||
type: user.type,
|
||||
};
|
||||
|
||||
//не следует создавать аккаунт в случаях, если он уже есть у пользователя
|
||||
|
||||
actions.push({
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.RegisterUser.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: registerUserData,
|
||||
});
|
||||
|
||||
// Проверяем наличие заявления на вступление и создаем joinCooperativeData
|
||||
const statement = await TempDocument.findOne({ username: user.username, type: tempdocType.JoinStatement });
|
||||
if (!statement) throw new ApiError(httpStatus.BAD_REQUEST, 'Не найдено заявление на вступление');
|
||||
|
||||
const joinCooperativeData: RegistratorContract.Actions.JoinCooperative.IJoinCooperative = {
|
||||
coopname: process.env.COOPNAME as string,
|
||||
registrator: process.env.COOPNAME as string,
|
||||
username: user.username,
|
||||
document: { ...user.statement, meta: JSON.stringify(user.statement.meta) },
|
||||
document: { ...statement.document, meta: JSON.stringify(statement.document.meta) },
|
||||
};
|
||||
actions.push({
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.JoinCooperative.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: joinCooperativeData,
|
||||
});
|
||||
|
||||
// Создаем createDeposit
|
||||
const createDeposit: GatewayContract.Actions.CreateDeposit.ICreateDeposit = {
|
||||
coopname: config.coopname,
|
||||
username: user.username,
|
||||
type: 'registration',
|
||||
quantity: order.quantity,
|
||||
deposit_id: order.order_num as number,
|
||||
};
|
||||
actions.push({
|
||||
account: GatewayContract.contractName.production,
|
||||
name: GatewayContract.Actions.CreateDeposit.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: createDeposit,
|
||||
});
|
||||
|
||||
// Создаем completeDeposit
|
||||
const completeDeposit: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit = {
|
||||
coopname: config.coopname,
|
||||
admin: config.coopname,
|
||||
deposit_id: order.order_num as number,
|
||||
memo: '',
|
||||
};
|
||||
actions.push({
|
||||
account: GatewayContract.contractName.production,
|
||||
name: GatewayContract.Actions.CompleteDeposit.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: completeDeposit,
|
||||
});
|
||||
|
||||
// Проверяем наличие соглашения на кошелек и создаем walletAgreementData
|
||||
const walletAgreement = await TempDocument.findOne({ username: user.username, type: tempdocType.WalletAgreement });
|
||||
if (!walletAgreement) throw new ApiError(httpStatus.BAD_REQUEST, 'Не найдено заявление на вступление');
|
||||
|
||||
const walletAgreementData: SovietContract.Actions.Agreements.SendAgreement.ISendAgreement = {
|
||||
coopname: process.env.COOPNAME as string,
|
||||
administrator: process.env.COOPNAME as string,
|
||||
username: user.username,
|
||||
agreement_type: 'wallet',
|
||||
document: { ...walletAgreement.document, meta: JSON.stringify(walletAgreement.document.meta) },
|
||||
};
|
||||
actions.push({
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Agreements.SendAgreement.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: walletAgreementData,
|
||||
});
|
||||
|
||||
// Проверяем наличие соглашения по ЭЦП и создаем signatureAgreementData
|
||||
const signatureAgreement = await TempDocument.findOne({ username: user.username, type: tempdocType.SignatureAgreement });
|
||||
if (!signatureAgreement) throw new ApiError(httpStatus.BAD_REQUEST, 'Не найдено соглашение о правилах использования ЭЦП');
|
||||
|
||||
const signatureAgreementData: SovietContract.Actions.Agreements.SendAgreement.ISendAgreement = {
|
||||
coopname: process.env.COOPNAME as string,
|
||||
administrator: process.env.COOPNAME as string,
|
||||
username: user.username,
|
||||
agreement_type: 'signature',
|
||||
document: { ...signatureAgreement.document, meta: JSON.stringify(signatureAgreement.document.meta) },
|
||||
};
|
||||
actions.push({
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Agreements.SendAgreement.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: signatureAgreementData,
|
||||
});
|
||||
|
||||
// Проверяем наличие соглашения о конфиденциальности и создаем privacyAgreementData
|
||||
const privacyAgreement = await TempDocument.findOne({ username: user.username, type: tempdocType.PrivacyAgreement });
|
||||
if (!privacyAgreement) throw new ApiError(httpStatus.BAD_REQUEST, 'Не найдено соглашение о политике конфиденциальности');
|
||||
|
||||
const privacyAgreementData: SovietContract.Actions.Agreements.SendAgreement.ISendAgreement = {
|
||||
coopname: process.env.COOPNAME as string,
|
||||
administrator: process.env.COOPNAME as string,
|
||||
username: user.username,
|
||||
agreement_type: 'privacy',
|
||||
document: { ...privacyAgreement.document, meta: JSON.stringify(privacyAgreement.document.meta) },
|
||||
};
|
||||
actions.push({
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Agreements.SendAgreement.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: privacyAgreementData,
|
||||
});
|
||||
|
||||
// Проверяем наличие пользовательского соглашения и создаем userAgreementData
|
||||
const userAgreement = await TempDocument.findOne({ username: user.username, type: tempdocType.UserAgreement });
|
||||
if (!userAgreement) throw new ApiError(httpStatus.BAD_REQUEST, 'Не найдено подписанное пользовательское соглашение');
|
||||
|
||||
const userAgreementData: SovietContract.Actions.Agreements.SendAgreement.ISendAgreement = {
|
||||
coopname: process.env.COOPNAME as string,
|
||||
administrator: process.env.COOPNAME as string,
|
||||
username: user.username,
|
||||
agreement_type: 'user',
|
||||
document: { ...userAgreement.document, meta: JSON.stringify(userAgreement.document.meta) },
|
||||
};
|
||||
actions.push({
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Agreements.SendAgreement.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: userAgreementData,
|
||||
});
|
||||
|
||||
const result = await eos.transact(
|
||||
{
|
||||
actions,
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
}
|
||||
);
|
||||
|
||||
// console.dir(result, { depth: null });
|
||||
}
|
||||
|
||||
async function createBoard(data: SovietContract.Actions.Boards.CreateBoard.ICreateboard) {
|
||||
const eos = await getInstance(config.coopname);
|
||||
|
||||
// console.log('data: ', data);
|
||||
|
||||
const actions = [
|
||||
{
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.CreateAccount.actionName,
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Boards.CreateBoard.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: process.env.COOPNAME as string,
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: newaccount,
|
||||
},
|
||||
{
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.RegisterUser.actionName, //reguser
|
||||
authorization: [
|
||||
{
|
||||
actor: process.env.COOPNAME as string,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: registerUserData,
|
||||
},
|
||||
{
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.JoinCooperative.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: process.env.COOPNAME as string,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: joinCooperativeData,
|
||||
},
|
||||
{
|
||||
account: GatewayContract.contractName.production,
|
||||
name: GatewayContract.Actions.CompleteDeposit.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: process.env.COOPNAME as string,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: orderData,
|
||||
data,
|
||||
},
|
||||
];
|
||||
|
||||
const result = await eos.transact(
|
||||
await eos.transact(
|
||||
{
|
||||
actions,
|
||||
},
|
||||
@@ -158,11 +364,11 @@ async function registerBlockchainAccount(user: IUser, orderData: GatewayContract
|
||||
}
|
||||
|
||||
async function createOrder(data) {
|
||||
const eos = await getInstance(process.env.REGISTRATOR_WIF);
|
||||
const eos = await getInstance(config.coopname);
|
||||
|
||||
const actions = [
|
||||
{
|
||||
account: process.env.GATEWAY_CONTRACT as string,
|
||||
account: GatewayContract.contractName.production,
|
||||
name: 'deposit',
|
||||
authorization: [
|
||||
{
|
||||
@@ -184,25 +390,51 @@ async function createOrder(data) {
|
||||
}
|
||||
);
|
||||
|
||||
const order_id = getInternalAction(result, 'newdepositid').id;
|
||||
const order_num = getInternalAction(result, 'newdepositid').id;
|
||||
|
||||
return order_id;
|
||||
return order_num;
|
||||
}
|
||||
|
||||
async function completeOrder(data: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit) {
|
||||
const eos = await getInstance(process.env.REGISTRATOR_WIF);
|
||||
async function completeDeposit(order: IOrder) {
|
||||
const eos = await getInstance(config.coopname);
|
||||
|
||||
const createDeposit: GatewayContract.Actions.CreateDeposit.ICreateDeposit = {
|
||||
coopname: config.coopname,
|
||||
username: order.username,
|
||||
type: 'deposit',
|
||||
quantity: order.quantity,
|
||||
deposit_id: order.order_num as number,
|
||||
};
|
||||
|
||||
const completeDeposit: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit = {
|
||||
coopname: config.coopname,
|
||||
admin: config.coopname,
|
||||
deposit_id: order.order_num as number,
|
||||
memo: '',
|
||||
};
|
||||
|
||||
const actions = [
|
||||
{
|
||||
account: GatewayContract.contractName.production,
|
||||
name: GatewayContract.Actions.CreateDeposit.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data: createDeposit,
|
||||
},
|
||||
{
|
||||
account: GatewayContract.contractName.production,
|
||||
name: GatewayContract.Actions.CompleteDeposit.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: process.env.COOPNAME as string,
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data,
|
||||
data: completeDeposit,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -218,11 +450,11 @@ async function completeOrder(data: GatewayContract.Actions.CompleteDeposit.IComp
|
||||
}
|
||||
|
||||
async function failOrder(data) {
|
||||
const eos = await getInstance(process.env.REGISTRATOR_WIF);
|
||||
const eos = await getInstance(config.coopname);
|
||||
|
||||
const actions = [
|
||||
{
|
||||
account: process.env.GATEWAY_CONTRACT as string,
|
||||
account: GatewayContract.contractName.production,
|
||||
name: 'dpfail',
|
||||
authorization: [
|
||||
{
|
||||
@@ -245,10 +477,114 @@ async function failOrder(data) {
|
||||
);
|
||||
}
|
||||
|
||||
export async function addUser(data: RegistratorContract.Actions.AddUser.IAddUser) {
|
||||
const eos = await getInstance(config.coopname);
|
||||
console.log('data:', data);
|
||||
const actions = [
|
||||
{
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.AddUser.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data,
|
||||
},
|
||||
];
|
||||
|
||||
await eos.transact(
|
||||
{
|
||||
actions,
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function changeKey(data: RegistratorContract.Actions.ChangeKey.IChangeKey) {
|
||||
const eos = await getInstance(config.coopname);
|
||||
|
||||
const actions = [
|
||||
{
|
||||
account: RegistratorContract.contractName.production,
|
||||
name: RegistratorContract.Actions.ChangeKey.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data,
|
||||
},
|
||||
];
|
||||
|
||||
await eos.transact(
|
||||
{
|
||||
actions,
|
||||
},
|
||||
{
|
||||
blocksBehind: 3,
|
||||
expireSeconds: 30,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function cancelOrder(data: GatewayContract.Actions.RefundDeposit.IRefundDeposit) {
|
||||
const action: IBCAction<GatewayContract.Actions.RefundDeposit.IRefundDeposit> = {
|
||||
account: GatewayContract.contractName.production,
|
||||
name: GatewayContract.Actions.RefundDeposit.actionName,
|
||||
authorization: [
|
||||
{
|
||||
actor: config.coopname,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data,
|
||||
};
|
||||
|
||||
return await transact([action]);
|
||||
}
|
||||
|
||||
export async function powerUp(username: string, quantity: string): Promise<void> {
|
||||
const data: SystemContract.Actions.Powerup.IPowerup = {
|
||||
payer: username,
|
||||
receiver: username,
|
||||
days: 1,
|
||||
payment: quantity,
|
||||
transfer: false,
|
||||
};
|
||||
|
||||
const actions = [
|
||||
{
|
||||
account: 'eosio',
|
||||
name: 'powerup',
|
||||
authorization: [
|
||||
{
|
||||
actor: username,
|
||||
permission: 'active',
|
||||
},
|
||||
],
|
||||
data,
|
||||
},
|
||||
];
|
||||
|
||||
try {
|
||||
const result = await transact(actions);
|
||||
console.log('Транзакция powerup выполнена:', result);
|
||||
} catch (error) {
|
||||
console.error('Ошибка при выполнении транзакции powerup:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function getSoviet(coopname) {
|
||||
const api = await getApi();
|
||||
|
||||
const soviet = (await lazyFetch(api, process.env.SOVIET_CONTRACT, coopname, 'boards'))[0];
|
||||
const soviet = (await lazyFetch(api, SovietContract.contractName.production, coopname, 'boards'))[0];
|
||||
|
||||
return soviet;
|
||||
}
|
||||
@@ -256,7 +592,7 @@ async function getSoviet(coopname) {
|
||||
async function fetchAllParticipants() {
|
||||
const api = await getApi();
|
||||
|
||||
const participants = await lazyFetch(api, process.env.SOVIET_CONTRACT, process.env.COOPNAME, 'participants');
|
||||
const participants = await lazyFetch(api, SovietContract.contractName.production, process.env.COOPNAME, 'participants');
|
||||
return participants;
|
||||
}
|
||||
|
||||
@@ -269,6 +605,10 @@ export {
|
||||
createOrder,
|
||||
getCooperative,
|
||||
failOrder,
|
||||
completeOrder,
|
||||
completeDeposit,
|
||||
getSoviet,
|
||||
getBlockchainInfo,
|
||||
getBlockchainAccount,
|
||||
hasActiveKey,
|
||||
createBoard,
|
||||
};
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
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[]> => {
|
||||
export const loadAgenda = async (coopname: string): Promise<Cooperative.Document.IAgenda[]> => {
|
||||
const api = await blockchainService.getApi();
|
||||
|
||||
const decisions = (await blockchainService.lazyFetch(
|
||||
api,
|
||||
process.env.SOVIET_CONTRACT as string,
|
||||
SovietContract.contractName.production as string,
|
||||
coopname,
|
||||
'decisions'
|
||||
)) as SovietContract.Tables.Decisions.IDecision[];
|
||||
|
||||
const agenda = [] as Cooperative.Documents.IAgenda[];
|
||||
const agenda = [] as Cooperative.Document.IAgenda[];
|
||||
|
||||
for (const table of decisions) {
|
||||
const action = (
|
||||
await getActions(`${process.env.SIMPLE_EXPLORER_API}/get-actions`, {
|
||||
filter: JSON.stringify({
|
||||
account: process.env.SOVIET_CONTRACT,
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Registry.NewSubmitted.actionName,
|
||||
receiver: process.env.COOPNAME,
|
||||
'data.decision_id': String(table.id),
|
||||
@@ -39,7 +40,7 @@ export const loadAgenda = async (coopname: string): Promise<Cooperative.Document
|
||||
export const loadStaff = async (coopname) => {
|
||||
const api = await blockchainService.getApi();
|
||||
|
||||
const staff = await blockchainService.lazyFetch(api, process.env.SOVIET_CONTRACT, coopname, 'staff');
|
||||
const staff = await blockchainService.lazyFetch(api, SovietContract.contractName.production, coopname, 'staff');
|
||||
|
||||
for (const staf of staff) {
|
||||
const user = await userService.getUserByUsername(staf.username);
|
||||
@@ -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,
|
||||
|
||||
+52
-53
@@ -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();
|
||||
|
||||
@@ -17,6 +11,10 @@ export const connectGenerator = async () => {
|
||||
await generator.connect(config.mongoose.url);
|
||||
};
|
||||
|
||||
export const diconnectGenerator = async () => {
|
||||
await generator.disconnect();
|
||||
};
|
||||
|
||||
export const generateDocument = async (options: IGenerate) => {
|
||||
return await generator.generate(options);
|
||||
};
|
||||
@@ -24,19 +22,27 @@ export const generateDocument = async (options: IGenerate) => {
|
||||
// Шаг 1: Создание новой функции для сборки complexDocument
|
||||
export async function buildComplexDocument(
|
||||
raw_action_document: Cooperative.Blockchain.IAction
|
||||
): Promise<Cooperative.Documents.IComplexDocument> {
|
||||
let statement = {} as Cooperative.Documents.IComplexStatement;
|
||||
let decision = {} as Cooperative.Documents.IComplexDecision;
|
||||
let act = {} as Cooperative.Documents.IComplexAct;
|
||||
): Promise<Cooperative.Document.IComplexDocument> {
|
||||
let statement = {} as Cooperative.Document.IComplexStatement;
|
||||
let decision = {} as Cooperative.Document.IComplexDecision;
|
||||
const act = {} as Cooperative.Document.IComplexAct;
|
||||
const links: Cooperative.Document.IGeneratedDocument[] = [];
|
||||
|
||||
const raw_document = raw_action_document.data as SovietContract.Actions.Registry.NewSubmitted.INewSubmitted;
|
||||
|
||||
// Готовим заявления
|
||||
{
|
||||
const document = await generator.getDocument({ hash: raw_document.document.hash });
|
||||
|
||||
if (document && document.meta.links && Array.isArray(document.meta.links))
|
||||
for (const link of document.meta.links) {
|
||||
const linked_document = await generator.getDocument({ hash: link });
|
||||
links.push(linked_document);
|
||||
}
|
||||
|
||||
const user = await User.findOne({ username: raw_document.username });
|
||||
|
||||
if (user) {
|
||||
if (user && user.type != 'service') {
|
||||
const user_data = await user?.getPrivateData();
|
||||
|
||||
const action: Cooperative.Blockchain.IExtendedAction = {
|
||||
@@ -45,8 +51,6 @@ export async function buildComplexDocument(
|
||||
};
|
||||
|
||||
statement = { action, document };
|
||||
} else {
|
||||
// throw new ApiError(400, 'Ошибка, один из пользователей не найден. Обратитесь в поддержку.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +61,7 @@ export async function buildComplexDocument(
|
||||
const decision_action = (
|
||||
await getActions(`${process.env.SIMPLE_EXPLORER_API}/get-actions`, {
|
||||
filter: JSON.stringify({
|
||||
account: process.env.SOVIET_CONTRACT,
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Registry.NewDecision.actionName,
|
||||
receiver: process.env.COOPNAME,
|
||||
'data.decision_id': String(raw_document.decision_id),
|
||||
@@ -71,8 +75,6 @@ export async function buildComplexDocument(
|
||||
const user = await User.findOne({ username: decision_action?.data?.username });
|
||||
|
||||
if (user) {
|
||||
const user_data = user.getPrivateData();
|
||||
|
||||
decision_extended_action = {
|
||||
...decision_action,
|
||||
user: (await user?.getPrivateData()) || null,
|
||||
@@ -80,6 +82,12 @@ export async function buildComplexDocument(
|
||||
|
||||
const document = await generator.getDocument({ hash: decision_action?.data?.document?.hash });
|
||||
|
||||
if (document && document.meta.links && Array.isArray(document.meta.links))
|
||||
for (const link of document.meta.links) {
|
||||
const linked_document = await generator.getDocument({ hash: link });
|
||||
links.push(linked_document);
|
||||
}
|
||||
|
||||
decision = {
|
||||
document,
|
||||
action: decision_extended_action,
|
||||
@@ -93,31 +101,46 @@ export async function buildComplexDocument(
|
||||
}
|
||||
|
||||
// Готовим акты
|
||||
const acts: Cooperative.Documents.IComplexAct[] = [];
|
||||
const acts: Cooperative.Document.IComplexAct[] = [];
|
||||
|
||||
return { statement, decision, acts };
|
||||
return { statement, decision, acts, links };
|
||||
}
|
||||
|
||||
function toDotNotation(obj, prefix = '') {
|
||||
return Object.keys(obj).reduce((acc, key) => {
|
||||
const value = obj[key];
|
||||
const newKey = prefix ? `${prefix}.${key}` : key;
|
||||
|
||||
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
||||
Object.assign(acc, toDotNotation(value, newKey));
|
||||
} else {
|
||||
acc[newKey] = value;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export const queryDocuments = async (
|
||||
type: string,
|
||||
filter: any,
|
||||
page: number = 1,
|
||||
limit: number = 100
|
||||
): Promise<Cooperative.Documents.IGetComplexDocuments> => {
|
||||
page = 1,
|
||||
limit = 100
|
||||
): Promise<Cooperative.Document.IGetComplexDocuments> => {
|
||||
const actions = await getActions<SovietContract.Actions.Registry.NewResolved.INewResolved>(
|
||||
`${process.env.SIMPLE_EXPLORER_API}/get-actions`,
|
||||
{
|
||||
filter: JSON.stringify({
|
||||
account: process.env.SOVIET_CONTRACT,
|
||||
name: SovietContract.Actions.Registry.NewResolved.actionName,
|
||||
receiver: process.env.COOPNAME,
|
||||
...filter,
|
||||
account: SovietContract.contractName.production,
|
||||
name: type, //< newresolved | newsubmitted
|
||||
...toDotNotation(filter),
|
||||
}),
|
||||
page,
|
||||
limit,
|
||||
}
|
||||
);
|
||||
|
||||
let response: Cooperative.Documents.IGetComplexDocuments = {
|
||||
const response: Cooperative.Document.IGetComplexDocuments = {
|
||||
results: [],
|
||||
page,
|
||||
limit,
|
||||
@@ -126,32 +149,8 @@ export const queryDocuments = async (
|
||||
for (const raw_action_document of actions.results) {
|
||||
const complexDocument = await buildComplexDocument(raw_action_document);
|
||||
|
||||
if (complexDocument.decision.action) response.results.push(complexDocument);
|
||||
if (complexDocument.statement.action) response.results.push(complexDocument);
|
||||
}
|
||||
|
||||
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,9 +2,9 @@ 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);
|
||||
export const transport = createTransport(email.smtp);
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (env !== 'test') {
|
||||
@@ -21,7 +21,7 @@ if (env !== 'test') {
|
||||
* @param {string} text
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const sendEmail = async (to, subject, text) => {
|
||||
export const sendEmail = async (to, subject, text) => {
|
||||
const msg = { from: email.from, to, subject, text };
|
||||
await transport.sendMail(msg);
|
||||
};
|
||||
@@ -32,13 +32,33 @@ const sendEmail = async (to, subject, text) => {
|
||||
* @param {string} token
|
||||
* @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.`;
|
||||
export const sendResetKeyEmail = async (to, token) => {
|
||||
const subject = 'Восстановление доступа';
|
||||
|
||||
const resetPasswordUrl = `${config.base_url}/#/${config.coopname}/auth/reset-key?token=${token}`;
|
||||
const text = `Мы получили запрос на перевыпуск приватного ключа,
|
||||
Для перевыпуска нажмите на ссылку: ${resetPasswordUrl}. Время действия ссылки - 10 минут.
|
||||
|
||||
Если вы не запрашивали перевыпуск ключа - проигнорируйте это сообщение.`;
|
||||
|
||||
await sendEmail(to, subject, text);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send reset password email
|
||||
* @param {string} to
|
||||
* @param {string} token
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const sendInviteEmail = async (to, token) => {
|
||||
const subject = 'Приглашение в Цифровой Кооператив';
|
||||
|
||||
const inviteUrl = `${config.base_url}/#/${config.coopname}/auth/reset-key?token=${token}`;
|
||||
const text = `Вам отправлено приглашение на подключение к Цифровому Кооперативу в качестве действующего пайщика.
|
||||
Для того, чтобы воспользоваться приглашением и получить ключ доступа, пожалуйста, нажмите на ссылку: ${inviteUrl}
|
||||
|
||||
Время действия ссылки - 24 часа.`;
|
||||
|
||||
await sendEmail(to, subject, text);
|
||||
};
|
||||
|
||||
@@ -48,7 +68,7 @@ If you did not request any password resets, then ignore this email.`;
|
||||
* @param {string} token
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const sendVerificationEmail = async (to, token) => {
|
||||
export const sendVerificationEmail = async (to, token) => {
|
||||
const subject = 'Email Verification';
|
||||
// replace this url with the link to the email verification page of your front-end app
|
||||
const verificationEmailUrl = `http://link-to-app/verify-email?token=${token}`;
|
||||
@@ -57,10 +77,3 @@ To verify your email, click on this link: ${verificationEmailUrl}
|
||||
If you did not create an account, then ignore this email.`;
|
||||
await sendEmail(to, subject, text);
|
||||
};
|
||||
|
||||
export {
|
||||
transport,
|
||||
sendEmail,
|
||||
sendResetPasswordEmail,
|
||||
sendVerificationEmail,
|
||||
};
|
||||
|
||||
@@ -4,6 +4,13 @@ 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 documentService from './document.service';
|
||||
export * as coopService from './coop.service';
|
||||
export * as wsService from './ws.service';
|
||||
export * as systemService from './system.service';
|
||||
export * as participantService from './participant.service';
|
||||
export * as redisService from './redis.service';
|
||||
export * as methodService from './method.service';
|
||||
export * as paymentService from './payment.service';
|
||||
export * as settingService from './setting.service';
|
||||
export * as pluginService from './plugin.service';
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { Cooperative } from 'cooptypes';
|
||||
import { generator } from './document.service';
|
||||
import type { FilterQuery } from '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);
|
||||
};
|
||||
@@ -1 +1 @@
|
||||
|
||||
export default {};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user