Compare commits
293 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3be56ba462 | |||
| f7adf8457b | |||
| 80b8856f15 | |||
| 0860f47d88 | |||
| 382b75fccf | |||
| d3dacbd32d | |||
| 6c51e4b596 | |||
| d0bd4428be | |||
| 43ffcb7362 | |||
| ca5dfca290 | |||
| 8a495eff37 | |||
| bee6af12fb | |||
| 4a3dfc3eb7 | |||
| 4e87a4adc4 | |||
| d9bb9d5884 | |||
| 562be0cb89 | |||
| 2fdc86af97 | |||
| 220d1225b2 | |||
| e047a6e449 | |||
| 97b21a9d69 | |||
| 9be9570dea | |||
| cbfe37dbba | |||
| bf176a2c05 | |||
| 173c51eb26 | |||
| 71dbb090fb | |||
| 832133e6cc | |||
| 2730a92b01 | |||
| 40fb5c80eb | |||
| 6c65095961 | |||
| d02f7ae7bc | |||
| 9f0b4b24ba | |||
| 237357c1d5 | |||
| aefb1caf57 | |||
| a3328e7ef0 | |||
| db24588273 | |||
| 2cabeeb6bd | |||
| 88352202b0 | |||
| e02d8a949b | |||
| e7e5457cf2 | |||
| 29b98b0f7d | |||
| ae7378f474 | |||
| b05c17bee4 | |||
| a2d3dac226 | |||
| a8ec41e255 | |||
| 1d16062961 | |||
| 2a614e8656 | |||
| baaaaf01bf | |||
| 20f1fc51b1 | |||
| d749ebf981 | |||
| 04364c503c | |||
| ae2a040fe4 | |||
| 0debf23b5e | |||
| 55e7849b43 | |||
| 34d9a73677 | |||
| 0b9ccf5bc0 | |||
| 48e72bb9df | |||
| 35aed44bcb | |||
| e4cf693c0d | |||
| f0755f5f90 | |||
| bdfc6af688 | |||
| 31b4061a43 | |||
| a1b80f0323 | |||
| 1678686ce7 | |||
| c25d05196d | |||
| bdddc7ad03 | |||
| 1d1ad4f9fa | |||
| 099fcc7981 | |||
| 2ef44e45f6 | |||
| ebe62d0498 | |||
| 6472b20ea4 | |||
| 69cdb2d754 | |||
| f7d8000b48 | |||
| a44afbe1b9 | |||
| 662da1b8a9 | |||
| 2a3d743204 | |||
| 7241ee4c7a | |||
| 5c084d6e8b | |||
| 1060c7fdfc | |||
| b5d6c0e0b3 | |||
| d69644350e | |||
| 8634622a6a | |||
| cc73554350 | |||
| 8adf93d592 | |||
| 6bd60ddf96 | |||
| ea5bcbe4dd | |||
| 541b49069d | |||
| ba868866ec | |||
| 606ae48f13 | |||
| 09c5445f83 | |||
| 4a0a18a7b0 | |||
| ed9d1ab738 | |||
| a61e8084dd | |||
| e169380178 | |||
| b07d4ef6c3 | |||
| 4c713c7652 | |||
| 90e8c6b3c4 | |||
| 07bf05cb2c | |||
| 49d29902a8 | |||
| 32a90c2a9e | |||
| 16ed2c1517 | |||
| 8bb0f1c799 | |||
| f14fe2dd64 | |||
| 884f0ee0de | |||
| 744270f48c | |||
| c30849be3c | |||
| f295ac8b4d | |||
| 9469a8f43d | |||
| 786799ca40 | |||
| aa6238cf5a | |||
| 1ed31ee2e2 | |||
| 7d8c12977c | |||
| 81ee1d63a6 | |||
| cf1c1b3198 | |||
| bc7b1e8472 | |||
| 7319c41cc1 | |||
| 2f09fc32c0 | |||
| f85b3f54a1 | |||
| 1a68669d6d | |||
| 6345c2d414 | |||
| 87930cad75 | |||
| 3ab2232121 | |||
| 9719e25421 | |||
| 8e1f998c39 | |||
| deb1c224f8 | |||
| b2fa79898a | |||
| da2759a3c6 | |||
| f6bc696426 | |||
| da653e9b32 | |||
| 227e153bde | |||
| 12f906b9b0 | |||
| c8236d4d10 | |||
| e2006da50d | |||
| 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 |
@@ -1,2 +1,3 @@
|
||||
node_modules/
|
||||
lerna-debug.log
|
||||
components/controller/graph.png
|
||||
|
||||
Vendored
+1
-1
@@ -6,7 +6,7 @@
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
|
||||
"eslint.validate": ["javascript", "typescript", "vue"],
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
// "typescript.tsdk": "node_modules/typescript/lib",
|
||||
"i18n-ally.localesPaths": ["src/i18n"],
|
||||
"typescript.format.enable": true,
|
||||
"typescript.format.indentSwitchCase": false,
|
||||
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.2.7](https://github.com/coopenomics/monocoop/compare/v2.2.6...v2.2.7) (2025-02-07)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.6](https://github.com/coopenomics/monocoop/compare/v2.2.6-alpha.0...v2.2.6) (2025-01-27)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.5](https://github.com/coopenomics/monocoop/compare/v2.2.4...v2.2.5) (2025-01-18)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.4](https://github.com/coopenomics/monocoop/compare/v2.2.0...v2.2.4) (2025-01-17)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.3](https://github.com/coopenomics/monocoop/compare/v2.2.0...v2.2.3) (2025-01-16)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.1](https://github.com/coopenomics/monocoop/compare/v2.2.0...v2.2.1) (2025-01-14)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.9](https://github.com/coopenomics/monocoop/compare/v2.1.8...v2.1.9) (2025-01-14)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.8](https://github.com/coopenomics/monocoop/compare/v2.1.6...v2.1.8) (2024-12-24)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.7](https://github.com/coopenomics/monocoop/compare/v2.1.6...v2.1.7) (2024-12-03)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.6](https://github.com/coopenomics/monocoop/compare/v2.1.5...v2.1.6) (2024-10-30)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.5](https://github.com/coopenomics/monocoop/compare/v2.1.4...v2.1.5) (2024-10-28)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.4](https://github.com/coopenomics/monocoop/compare/v2.1.4-alpha.2...v2.1.4) (2024-10-28)
|
||||
|
||||
**Note:** Version bump only for package monocoop
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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`
|
||||
|
||||
@@ -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.
Vendored
BIN
Binary file not shown.
@@ -5,11 +5,7 @@
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"],
|
||||
"plugins": ["@typescript-eslint", "prettier"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
@@ -18,7 +14,8 @@
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "warn",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"no-console": "off",
|
||||
"func-names": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"consistent-return": "off",
|
||||
@@ -12,4 +12,7 @@ yarn-error.log
|
||||
coverage
|
||||
|
||||
.env-mixed
|
||||
.env-local
|
||||
.env-local
|
||||
|
||||
logs
|
||||
logs/*
|
||||
@@ -3,6 +3,178 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.2.7](https://github.com/hagopj13/node-express-boilerplate/compare/v2.2.6...v2.2.7) (2025-02-07)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.6](https://github.com/hagopj13/node-express-boilerplate/compare/v2.2.6-alpha.0...v2.2.6) (2025-01-27)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.5](https://github.com/hagopj13/node-express-boilerplate/compare/v2.2.4...v2.2.5) (2025-01-18)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.4](https://github.com/hagopj13/node-express-boilerplate/compare/v2.2.0...v2.2.4) (2025-01-17)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.3](https://github.com/hagopj13/node-express-boilerplate/compare/v2.2.0...v2.2.3) (2025-01-16)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.1](https://github.com/hagopj13/node-express-boilerplate/compare/v2.2.0...v2.2.1) (2025-01-14)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.9](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.8...v2.1.9) (2025-01-14)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.8](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.6...v2.1.8) (2024-12-24)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.7](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.6...v2.1.7) (2024-12-03)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.6](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.5...v2.1.6) (2024-10-30)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.5](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.4...v2.1.5) (2024-10-28)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.4](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.4-alpha.2...v2.1.4) (2024-10-28)
|
||||
|
||||
**Note:** Version bump only for package @coopenomics/controller
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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
|
||||
@@ -0,0 +1,40 @@
|
||||
# Базовый образ Node.js на Alpine
|
||||
FROM node:alpine AS base
|
||||
|
||||
# Включение pnpm
|
||||
# Устанавливаем pnpm без использования corepack
|
||||
RUN npm install -g pnpm@8.15.4
|
||||
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем package.json
|
||||
COPY package.json ./
|
||||
|
||||
# Установка системных зависимостей для WeasyPrint и создание виртуального окружения
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
py3-pip \
|
||||
gcc \
|
||||
musl-dev \
|
||||
python3-dev \
|
||||
pango \
|
||||
zlib-dev \
|
||||
jpeg-dev \
|
||||
openjpeg-dev \
|
||||
g++ \
|
||||
libffi-dev \
|
||||
harfbuzz-subset \
|
||||
&& python3 -m venv /venv \
|
||||
&& /venv/bin/pip install WeasyPrint==62.3 \
|
||||
&& rm -rf /var/cache/*
|
||||
|
||||
# Установка пути к виртуальному окружению в PATH
|
||||
ENV PATH="/venv/bin:$PATH"
|
||||
|
||||
# Проверка наличия команды weasyprint
|
||||
RUN weasyprint --version
|
||||
|
||||
# CMD для запуска weasyprint
|
||||
# CMD ["weasyprint", "input.html", "output.pdf"]
|
||||
@@ -0,0 +1 @@
|
||||
# CONTROLLER
|
||||
@@ -0,0 +1,14 @@
|
||||
overwrite: true
|
||||
schema: './schema.gql' # Укажите путь к вашей схеме (или URL сервера)
|
||||
documents: './src/modules/**/operations/*.graphql' # Путь, где будут храниться запросы
|
||||
generates:
|
||||
../coopjs/src/sdk/index.ts:
|
||||
plugins:
|
||||
- 'typescript'
|
||||
- 'typescript-operations'
|
||||
- 'typescript-graphql-request' # Плагин для генерации клиента
|
||||
config:
|
||||
rawRequest: true
|
||||
namingConvention:
|
||||
typeNames: keep # Оставляем типы без изменений
|
||||
transformUnderscore: true
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "create-nodejs-express-app",
|
||||
"version": "1.7.35-alpha.4",
|
||||
"version": "2.2.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "create-nodejs-express-app",
|
||||
"version": "1.7.35-alpha.4",
|
||||
"version": "2.2.7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@a2seven/yoo-checkout": "^1.1.4",
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "coopback",
|
||||
"version": "1.7.35-alpha.4",
|
||||
"name": "@coopenomics/controller",
|
||||
"version": "2.2.7",
|
||||
"description": "",
|
||||
"private": true,
|
||||
"private": false,
|
||||
"bin": "bin/createNodejsApp.js",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/hagopj13/node-express-boilerplate.git",
|
||||
@@ -12,11 +12,14 @@
|
||||
"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\"",
|
||||
"start": "ts-node -r tsconfig-paths/register src/index.ts",
|
||||
"dev": "concurrently -n 'CONTROLLER' -c 'bgCyan.black' \"nodemon --watch src --ext ts,js,env --exec 'ts-node -r tsconfig-paths/register' src/index.ts\"",
|
||||
"test": "jest -i --colors --testTimeout=120000",
|
||||
"test:watch": "jest -i --watchAll",
|
||||
"coverage": "jest -i --coverage",
|
||||
@@ -33,7 +36,9 @@
|
||||
"swagger": "ts-node scripts/swagger.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"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"
|
||||
"docs": "spectaql spectaql-config.yml",
|
||||
"docs2": "ts-node scripts/swagger.ts && redoc-cli bundle ./src/docs/swagger.json -o ./docs/index.html",
|
||||
"generate-client": "zeus ./schema.gql ../sdk/src --subscriptions graphql-ws && cp -r ./src/types/shared/* ../sdk/src/types/controller"
|
||||
},
|
||||
"keywords": [
|
||||
"node",
|
||||
@@ -58,13 +63,34 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@a2seven/yoo-checkout": "^1.1.4",
|
||||
"@coopenomics/factory": "workspace:*",
|
||||
"@graphql-codegen/typescript-graphql-request": "^6.2.0",
|
||||
"@graphql-codegen/typescript-resolvers": "^4.4.0",
|
||||
"@graphql-tools/schema": "^10.0.7",
|
||||
"@graphql-tools/utils": "^10.5.5",
|
||||
"@nestjs/apollo": "^12.2.1",
|
||||
"@nestjs/bull": "^10.2.1",
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/config": "^3.2.3",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
"@nestjs/graphql": "^12.2.1",
|
||||
"@nestjs/mongoose": "^10.1.0",
|
||||
"@nestjs/passport": "^10.0.3",
|
||||
"@nestjs/platform-express": "^10.0.0",
|
||||
"@nestjs/throttler": "^6.3.0",
|
||||
"@nestjs/typeorm": "^10.0.2",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/supertest": "^6.0.2",
|
||||
"@wharfkit/antelope": "^1.0.7",
|
||||
"@wharfkit/contract": "^1.1.5",
|
||||
"@wharfkit/session": "^1.4.0",
|
||||
"@wharfkit/wallet-plugin-privatekey": "^1.1.0",
|
||||
"apollo-server-express": "^3.13.0",
|
||||
"axios": "^1.6.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"compression": "^1.7.4",
|
||||
"coopdoc-generator-ts": "workspace:*",
|
||||
"cooptypes": "workspace:*",
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "^7.0.0",
|
||||
@@ -78,37 +104,66 @@
|
||||
"express-mongo-sanitize": "^2.0.0",
|
||||
"express-rate-limit": "^5.0.0",
|
||||
"faker": "^5.1.0",
|
||||
"glob": "^11.0.0",
|
||||
"graphql": "^16.9.0",
|
||||
"graphql-request": "^7.1.2",
|
||||
"graphql-scalars": "^1.24.0",
|
||||
"graphql-tools": "^9.0.2",
|
||||
"graphql-type-json": "^0.3.2",
|
||||
"graphql-ws": "^5.16.0",
|
||||
"helmet": "^4.1.0",
|
||||
"html-pdf-node": "^1.0.8",
|
||||
"http-proxy-middleware": "^3.0.3",
|
||||
"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": "^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",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pm2": "^5.1.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"spectaql": "^3.0.2",
|
||||
"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",
|
||||
"typedoc": "^0.25.13",
|
||||
"typeorm": "0.3.20",
|
||||
"uuid": "^11.0.0",
|
||||
"validator": "^13.0.0",
|
||||
"winston": "^3.2.1",
|
||||
"xss-clean": "^0.1.1"
|
||||
"winston-daily-rotate-file": "^5.0.0",
|
||||
"ws": "^8.18.0",
|
||||
"xss-clean": "^0.1.1",
|
||||
"zeus": "^3.3.2",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.23.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^2.24.1",
|
||||
"@graphql-codegen/cli": "^5.0.3",
|
||||
"@graphql-codegen/typescript": "^4.1.1",
|
||||
"@graphql-codegen/typescript-apollo-client-helpers": "^3.0.0",
|
||||
"@graphql-codegen/typescript-operations": "^4.3.1",
|
||||
"@redocly/cli": "^1.18.0",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/faker": "^6.6.9",
|
||||
@@ -124,10 +179,13 @@
|
||||
"eslint-plugin-jest": "^24.0.1",
|
||||
"eslint-plugin-prettier": "^3.1.1",
|
||||
"eslint-plugin-security": "^1.4.0",
|
||||
"graphql-zeus": "^7.0.1",
|
||||
"node-mocks-http": "^1.8.0",
|
||||
"nodemon": "^2.0.0",
|
||||
"prettier": "^2.0.5",
|
||||
"supertest": "^6.0.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "^5.4.2"
|
||||
}
|
||||
},
|
||||
"gitHead": "b05c17bee481d90c4cd82aa2a34ac428f8263a5f"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,14 +27,13 @@ async function init() {
|
||||
last_name: 'Иванов',
|
||||
middle_name: 'Иванович',
|
||||
birthdate: '2000-01-01',
|
||||
phone: '+1234567890',
|
||||
phone: '+72234567890',
|
||||
email: 'ivanov.ivan@example.com',
|
||||
full_address: 'Переулок Иванов д. 1',
|
||||
},
|
||||
});
|
||||
|
||||
const user = await userService.getUserByUsername('ant');
|
||||
user.status = 'active';
|
||||
user.is_registered = true;
|
||||
await user.save();
|
||||
|
||||
@@ -45,7 +44,6 @@ async function init() {
|
||||
role: 'user',
|
||||
type: 'organization',
|
||||
organization_data: {
|
||||
is_cooperative: true,
|
||||
type: 'coop',
|
||||
short_name: '"ПК ТЕСТ-Восход"',
|
||||
full_name: 'Потребительский Кооператив "ТЕСТ-ВОСХОД"',
|
||||
@@ -62,13 +60,14 @@ async function init() {
|
||||
email: 'chairman.voskhod@gmail.com',
|
||||
phone: '+77077770707',
|
||||
details: {
|
||||
inn: '1234567890',
|
||||
ogrn: '1234567890123',
|
||||
kpp: '123456789',
|
||||
inn: '71234567890',
|
||||
ogrn: '71234567890123',
|
||||
},
|
||||
bank_account: {
|
||||
account_number: '40817810099910004312',
|
||||
currency: 'RUB',
|
||||
card_number: '1234567890123456',
|
||||
card_number: '71234567890123456',
|
||||
bank_name: 'Sberbank',
|
||||
details: {
|
||||
bik: '123456789',
|
||||
@@ -0,0 +1,34 @@
|
||||
spectaql:
|
||||
targetDir: ../docs/graphql
|
||||
introspection:
|
||||
url: 'http://localhost:2998/v1/graphql'
|
||||
|
||||
info:
|
||||
title: 'API-Документация'
|
||||
description: 'MONO - автономная и единая точка доступа к кооперативной экономике. MONO предоставляет методы для извлечения (QUERIES), изменения (MUTATIONS) и подписок (SUBSCRIPTIONS) на информацию в локальной базе данных кооператива и распределенной базе данных кооперативной экономики, используя технологию GRAPHQL.'
|
||||
# This is non-standard and optional. If omitted, will use "title". Also, only relevant
|
||||
# when building non-embedded.
|
||||
x-htmlTitle: Your HTML page title here
|
||||
termsOfService: 'https://цифровой-кооператив.рф'
|
||||
contact:
|
||||
# name: YourSite Support
|
||||
email: support@coopenomics.world
|
||||
# url: https://yoursite.com/support
|
||||
license:
|
||||
name: BY-NC-SA 4.0
|
||||
url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.ru'
|
||||
|
||||
# x-introItems:
|
||||
# Can be a Title (for the Nav panel) + URL to simply add a link to somewhere
|
||||
# - title: A silly link to our website!
|
||||
# url: http://yoursite.com
|
||||
# Can be a Title (for the Nav panel) + description (for the Content panel)
|
||||
# - title: Important thing 1
|
||||
# description: Some important stuff we wanted you to know. Supports `markdown`
|
||||
# # Can be a Title (for the Nav panel) + file that gets loaded (for the Content panel)
|
||||
# - title: Content from file that supports markdown
|
||||
# file: ./examples/data/intro-items-markdown.md
|
||||
|
||||
# If you really want to hide the "Documentation by" at the bottom of your output, you can do so here
|
||||
# Default: false
|
||||
x-hidePoweredBy: true
|
||||
@@ -0,0 +1,8 @@
|
||||
# TEST
|
||||
|
||||
testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest
|
||||
test
|
||||
st
|
||||
|
||||
test
|
||||
st
|
||||
Binary file not shown.
@@ -0,0 +1,27 @@
|
||||
// app.module.ts
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { DomainModule } from './domain/domain.module';
|
||||
import { ApplicationModule } from './modules/application.module';
|
||||
import { InfrastructureModule } from './infrastructure/infrastructure.module';
|
||||
import { ThrottlerModule } from '@nestjs/throttler';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true, // Чтобы .env был доступен глобально
|
||||
}),
|
||||
ThrottlerModule.forRoot([
|
||||
{
|
||||
ttl: 60000,
|
||||
limit: 50,
|
||||
},
|
||||
]),
|
||||
DomainModule,
|
||||
ApplicationModule,
|
||||
InfrastructureModule,
|
||||
],
|
||||
providers: [],
|
||||
exports: [],
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -14,14 +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(() => {})
|
||||
// .catch((error) => {
|
||||
// console.error('Failed to connect generator:', error);
|
||||
// });
|
||||
|
||||
const app = express();
|
||||
|
||||
if (config.env !== 'test') {
|
||||
@@ -30,7 +22,7 @@ if (config.env !== 'test') {
|
||||
}
|
||||
|
||||
// set security HTTP headers
|
||||
app.use(helmet());
|
||||
app.use(helmet({hsts: false}));
|
||||
|
||||
// parse json request body
|
||||
app.use(express.json());
|
||||
@@ -61,8 +53,12 @@ if (config.env === 'production') {
|
||||
// v1 api routes
|
||||
app.use('/v1', routes);
|
||||
|
||||
// send back a 404 error for any unknown api request
|
||||
// Обработка остальных маршрутов, включая неизвестные
|
||||
app.use((req, res, next) => {
|
||||
if (req.path === '/v1/graphql') {
|
||||
// Пропустить обработку для маршрута /graphql и позволить NestJS обработать запрос
|
||||
return next();
|
||||
}
|
||||
next(new ApiError(httpStatus.NOT_FOUND, 'Not found'));
|
||||
});
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import dotenv from 'dotenv';
|
||||
import { z } from 'zod';
|
||||
import path from 'path';
|
||||
|
||||
dotenv.config({ path: path.join(__dirname, '../../.env') });
|
||||
|
||||
const envVarsSchema = z.object({
|
||||
NODE_ENV: z.enum(['production', 'development', 'test']),
|
||||
BASE_URL: z.string(),
|
||||
SERVER_SECRET: z.string(),
|
||||
PORT: z
|
||||
.string()
|
||||
.default('3000')
|
||||
.transform((val) => parseInt(val, 10)),
|
||||
MONGODB_URL: z.string().describe('Mongo DB url'),
|
||||
JWT_SECRET: z.string().describe('JWT secret key'),
|
||||
JWT_ACCESS_EXPIRATION_MINUTES: z
|
||||
.string()
|
||||
.default('30')
|
||||
.transform((val) => parseInt(val, 10)),
|
||||
JWT_REFRESH_EXPIRATION_DAYS: z
|
||||
.string()
|
||||
.default('30')
|
||||
.transform((val) => parseInt(val, 10)),
|
||||
JWT_RESET_PASSWORD_EXPIRATION_MINUTES: z
|
||||
.string()
|
||||
.default('10')
|
||||
.transform((val) => parseInt(val, 10)),
|
||||
JWT_VERIFY_EMAIL_EXPIRATION_MINUTES: z
|
||||
.string()
|
||||
.default('10')
|
||||
.transform((val) => parseInt(val, 10)),
|
||||
JWT_INVITE_EXPIRATION_MINUTES: z
|
||||
.string()
|
||||
.default('3600')
|
||||
.transform((val) => parseInt(val, 10)),
|
||||
SMTP_HOST: z.string().default(''), // Задаём пустую строку по умолчанию
|
||||
SMTP_PORT: z
|
||||
.string()
|
||||
.default('587') // Пример порта по умолчанию
|
||||
.transform((val) => parseInt(val, 10)),
|
||||
SMTP_USERNAME: z.string().default(''), // Пустая строка для необязательного значения
|
||||
SMTP_PASSWORD: z.string().default(''), // Пустая строка для необязательного значения
|
||||
EMAIL_FROM: z.string().default(''), // Пустая строка для необязательного значения
|
||||
COOPNAME: z.string().default('default_coopname'), // Задаём дефолтное значение
|
||||
GRAPHQL_SERVICE: z.string().default('http://localhost:4090').describe('адрес сервиса GRAPHQL'),
|
||||
|
||||
// Новые переменные для PostgreSQL
|
||||
POSTGRES_HOST: z.string().default('127.0.0.1'),
|
||||
POSTGRES_PORT: z
|
||||
.string()
|
||||
.default('5432')
|
||||
.transform((val) => parseInt(val, 10)),
|
||||
POSTGRES_USERNAME: z.string(),
|
||||
POSTGRES_PASSWORD: z.string(),
|
||||
POSTGRES_DATABASE: z.string(),
|
||||
|
||||
REDIS_HOST: z.string(),
|
||||
REDIS_PORT: z
|
||||
.string()
|
||||
.default('6379')
|
||||
.transform((val) => parseInt(val, 10)),
|
||||
REDIS_PASSWORD: z.string(),
|
||||
BLOCKCHAIN_RPC: z.string(),
|
||||
CHAIN_ID: z.string(),
|
||||
});
|
||||
|
||||
// Валидация переменных окружения
|
||||
const envVars = envVarsSchema.safeParse(process.env);
|
||||
|
||||
if (!envVars.success) {
|
||||
const formattedErrors = envVars.error.format();
|
||||
|
||||
const errorMessages = Object.keys(formattedErrors)
|
||||
.filter((key) => key !== '_errors')
|
||||
.map((key) => `${key}: параметр не установлен`)
|
||||
.join('\n');
|
||||
|
||||
console.error('❌ Ошибка конфигурации:\n', errorMessages);
|
||||
process.exit(1); // Завершаем приложение в случае ошибки
|
||||
}
|
||||
|
||||
// Экспорт настроек
|
||||
export default {
|
||||
env: envVars.data.NODE_ENV,
|
||||
base_url: envVars.data.BASE_URL,
|
||||
port: envVars.data.PORT,
|
||||
server_secret: envVars.data.SERVER_SECRET,
|
||||
blockchain: {
|
||||
url: envVars.data.BLOCKCHAIN_RPC,
|
||||
id: envVars.data.CHAIN_ID,
|
||||
},
|
||||
mongoose: {
|
||||
url: envVars.data.MONGODB_URL + (envVars.data.NODE_ENV === 'test' ? '-test' : ''),
|
||||
options: {
|
||||
// useCreateIndex: true,
|
||||
// useNewUrlParser: true,
|
||||
// useUnifiedTopology: true,
|
||||
},
|
||||
},
|
||||
jwt: {
|
||||
secret: envVars.data.JWT_SECRET,
|
||||
accessExpirationMinutes: envVars.data.JWT_ACCESS_EXPIRATION_MINUTES,
|
||||
refreshExpirationDays: envVars.data.JWT_REFRESH_EXPIRATION_DAYS,
|
||||
resetPasswordExpirationMinutes: envVars.data.JWT_RESET_PASSWORD_EXPIRATION_MINUTES,
|
||||
verifyEmailExpirationMinutes: envVars.data.JWT_VERIFY_EMAIL_EXPIRATION_MINUTES,
|
||||
inviteExpirationMinutes: envVars.data.JWT_INVITE_EXPIRATION_MINUTES,
|
||||
},
|
||||
email: {
|
||||
smtp: {
|
||||
host: envVars.data.SMTP_HOST,
|
||||
port: envVars.data.SMTP_PORT,
|
||||
auth: {
|
||||
user: envVars.data.SMTP_USERNAME,
|
||||
pass: envVars.data.SMTP_PASSWORD,
|
||||
},
|
||||
},
|
||||
from: envVars.data.EMAIL_FROM,
|
||||
},
|
||||
coopname: envVars.data.COOPNAME,
|
||||
graphql_service: envVars.data.GRAPHQL_SERVICE,
|
||||
postgres: {
|
||||
host: envVars.data.POSTGRES_HOST,
|
||||
port: envVars.data.POSTGRES_PORT,
|
||||
username: envVars.data.POSTGRES_USERNAME,
|
||||
password: envVars.data.POSTGRES_PASSWORD,
|
||||
database: envVars.data.POSTGRES_DATABASE,
|
||||
},
|
||||
redis: {
|
||||
host: envVars.data.REDIS_HOST,
|
||||
port: envVars.data.REDIS_PORT,
|
||||
password: envVars.data.REDIS_PASSWORD,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import winston from 'winston';
|
||||
import DailyRotateFile from 'winston-daily-rotate-file';
|
||||
import config from './config';
|
||||
|
||||
const enumerateErrorFormat = winston.format((info) => {
|
||||
if (info instanceof Error) {
|
||||
Object.assign(info, { message: info.stack });
|
||||
}
|
||||
return 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(),
|
||||
winston.format.colorize(),
|
||||
winston.format.splat(),
|
||||
winston.format.printf(({ timestamp, level, message, context, meta, ...restMeta }) => {
|
||||
// Проверяем, является ли meta строкой
|
||||
const contextString = typeof meta === 'string' ? `[${meta}]` : context ? `[${context}]` : '';
|
||||
const metaString = typeof meta === 'object' && Object.keys(meta).length ? ` - ${JSON.stringify(meta)}` : '';
|
||||
const additionalMetaString = Object.keys(restMeta).length ? ` - ${JSON.stringify(restMeta)}` : '';
|
||||
|
||||
return `${timestamp} ${level}: ${contextString} ${message}${metaString}${additionalMetaString}`;
|
||||
})
|
||||
),
|
||||
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',
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export default logger;
|
||||
+11
-5
@@ -1,10 +1,16 @@
|
||||
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 || '');
|
||||
|
||||
const getIpFormat = () => (config.env === 'production' ? ':remote-addr - ' : '');
|
||||
// Новый токен для определения IP с проверкой нескольких заголовков
|
||||
morgan.token('client-ip', (req) => {
|
||||
return req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
||||
});
|
||||
|
||||
// Форматы с использованием нового токена 'client-ip'
|
||||
const getIpFormat = () => (config.env === 'production' ? ':client-ip - ' : ':remote-addr - ');
|
||||
const successResponseFormat = `${getIpFormat()}:method :url :status - :response-time ms`;
|
||||
const errorResponseFormat = `${getIpFormat()}:method :url :status - :response-time ms - message: :message`;
|
||||
|
||||
@@ -21,4 +27,4 @@ const errorHandler = morgan(errorResponseFormat, {
|
||||
export default {
|
||||
successHandler,
|
||||
errorHandler,
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
const user = [
|
||||
'getSelf',
|
||||
'initialPayment',
|
||||
'getMyDocuments',
|
||||
'getMyOrders',
|
||||
'sendVerificationEmail',
|
||||
'manageMyMethods',
|
||||
'createDeposit',
|
||||
'joinCooperative',
|
||||
'getVars',
|
||||
'readGraph',
|
||||
];
|
||||
|
||||
const member = [
|
||||
...user,
|
||||
'getUsers',
|
||||
'manageUsers',
|
||||
'loadAgenda',
|
||||
'loadStaff',
|
||||
'getDocuments',
|
||||
'loadInfo',
|
||||
'manageOrders',
|
||||
'regenerateDocument',
|
||||
];
|
||||
|
||||
const chairman = [...user, ...member, 'addUser', 'setVars', 'manageSettings', 'setWif', 'setPlugin'];
|
||||
|
||||
const allRoles = {
|
||||
user,
|
||||
member,
|
||||
chairman,
|
||||
};
|
||||
|
||||
export const roles = Object.keys(allRoles);
|
||||
export const roleRights = new Map(Object.entries(allRoles));
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
export const tokenTypes = {
|
||||
ACCESS: 'access',
|
||||
REFRESH: 'refresh',
|
||||
RESET_PASSWORD: 'resetPassword',
|
||||
RESET_KEY: 'resetPassword',
|
||||
VERIFY_EMAIL: 'verifyEmail',
|
||||
INVITE: 'invite',
|
||||
};
|
||||
+2
-2
@@ -14,8 +14,8 @@ export const getDocuments = catchAsync(async (req: RGetDocuments, res) => {
|
||||
const filter = req.query?.filter;
|
||||
const limit = req.query?.limit as number;
|
||||
const page = req.query?.page as number;
|
||||
|
||||
const documents = await documentService.queryDocuments(filter, page, limit);
|
||||
const type = req.query?.type as string;
|
||||
const documents = await documentService.queryDocuments(type, filter, page, limit);
|
||||
|
||||
res.status(httpStatus.OK).send(documents);
|
||||
});
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
export * as authController from './auth.controller';
|
||||
export * as userController from './user.controller';
|
||||
export * as documentController from './document.controller';
|
||||
export * as paymentController from './payment.controller';
|
||||
export * as orderController from './order.controller';
|
||||
export * as coopController from './coop.controller';
|
||||
export * as notifyController from './notify.controller';
|
||||
export * as monoController from './mono.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,54 @@
|
||||
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: String(req.body.method_id),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-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();
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import http from 'http-status';
|
||||
import catchAsync from '../utils/catchAsync';
|
||||
import { orderService } from '../services';
|
||||
import { ICreateDeposit } from '../types';
|
||||
import { Request, Response } from 'express';
|
||||
import httpStatus from 'http-status';
|
||||
import pick from '../utils/pick';
|
||||
import logger from '../config/logger';
|
||||
import { ProviderInteractor } from '~/domain/provider/provider.interactor';
|
||||
import { nestApp } from '..';
|
||||
|
||||
export const catchIPN = catchAsync(async (req: Request, res: Response) => {
|
||||
const providerName = req.params.provider;
|
||||
logger.info(`Recieve new IPN for provider ${providerName}`, { body: req.body, source: 'catchIPN' });
|
||||
|
||||
const providerInteractor = nestApp.get(ProviderInteractor);
|
||||
const provider = providerInteractor.getProvider(providerName);
|
||||
|
||||
// Обрабатываем 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 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);
|
||||
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,57 @@
|
||||
import { 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 pick from '../utils/pick';
|
||||
import { ExtensionDomainInteractor } from '~/domain/extension/interactors/extension-domain.interactor';
|
||||
import { nestApp } from '..';
|
||||
|
||||
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);
|
||||
|
||||
const extensionDomainInteractor = nestApp.get(ExtensionDomainInteractor);
|
||||
|
||||
if (enabled == false) await extensionDomainInteractor.terminateApp(name);
|
||||
else await extensionDomainInteractor.restartApp(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;
|
||||
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);
|
||||
});
|
||||
+38
-43
@@ -5,12 +5,26 @@ import { userService, tokenService, emailService, blockchainService } from '../s
|
||||
import { IAddUser, ICreateUser, RCreateUser } from '../types';
|
||||
import httpStatus from 'http-status';
|
||||
import pick from '../utils/pick';
|
||||
import { IGetResponse } from '../types/common';
|
||||
import { IUser, userStatus } from '../models/user.model';
|
||||
// import { IGetResponse } from '../types/common';
|
||||
import { IUser, userStatus } from '../types';
|
||||
import { Request, Response } from 'express';
|
||||
import { generateUsername } from '../../tests/utils/generateUsername';
|
||||
import { generateUsername } from '../utils/generate-username';
|
||||
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 }; // Возвращаем объект в ответ
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Порядок регистрации:
|
||||
@@ -27,6 +41,7 @@ import logger from '../config/logger';
|
||||
* 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);
|
||||
@@ -47,13 +62,15 @@ export const addUser = catchAsync(async (req: Request, res: Response) => {
|
||||
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.service_username,
|
||||
registrator: config.coopname,
|
||||
referer: body.referer ? body.referer : '',
|
||||
coopname: config.coopname,
|
||||
meta: '',
|
||||
@@ -70,45 +87,6 @@ export const addUser = catchAsync(async (req: Request, res: Response) => {
|
||||
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']);
|
||||
|
||||
const users = await userService.queryUsers(filter, options);
|
||||
const result = {} as IGetResponse<IUser>;
|
||||
|
||||
const data = [] as any;
|
||||
for await (const user of users.results) {
|
||||
const json = user.toJSON();
|
||||
if (user.type != 'service') {
|
||||
json.private_data = await user.getPrivateData();
|
||||
data.push(json);
|
||||
}
|
||||
}
|
||||
|
||||
result.results = data;
|
||||
result.limit = users.limit;
|
||||
result.page = users.page;
|
||||
result.totalPages = users.totalPages;
|
||||
result.totalResults = users.totalResults;
|
||||
|
||||
res.send(result);
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
if (user.type != 'service') {
|
||||
json.private_data = await user.getPrivateData();
|
||||
}
|
||||
|
||||
res.send(json);
|
||||
});
|
||||
|
||||
export const updateUser = catchAsync(async (req, res) => {
|
||||
if (req.user.role !== 'member' && req.user.role !== 'chairman')
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Только председатель или член совета может обновить данные пользователя');
|
||||
@@ -118,6 +96,23 @@ export const updateUser = catchAsync(async (req, res) => {
|
||||
});
|
||||
|
||||
export const deleteUser = catchAsync(async (req, res) => {
|
||||
if (req.user.role !== 'member' && req.user.role !== 'chairman')
|
||||
throw new ApiError(httpStatus.FORBIDDEN, 'Только председатель или член совета может обновить данные пользователя');
|
||||
|
||||
await userService.deleteUserByUsername(req.params.username);
|
||||
res.status(http.NO_CONTENT).send();
|
||||
});
|
||||
|
||||
export const getUsers = catchAsync(async (req, res) => {
|
||||
const filter = pick(req.query, ['username', 'role']);
|
||||
const options = pick(req.query, ['sortBy', 'limit', 'page']);
|
||||
|
||||
const users = await userService.queryUsers(filter, options);
|
||||
res.send(users);
|
||||
});
|
||||
|
||||
export const getUser = catchAsync(async (req, res) => {
|
||||
const user = await userService.getUserByUsername(req.params.username);
|
||||
|
||||
res.send(user);
|
||||
});
|
||||
+13
-5
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-70
@@ -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",
|
||||
+1
-1
@@ -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,12 @@
|
||||
// domain/account/account-domain.module.ts
|
||||
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AccountDomainInteractor } from './interactors/account.interactor';
|
||||
import { AccountDomainService } from '~/domain/account/services/account-domain.service';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
providers: [AccountDomainService, AccountDomainInteractor],
|
||||
exports: [AccountDomainInteractor, AccountDomainService],
|
||||
})
|
||||
export class AccountDomainModule {}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { RegistratorContract, type SovietContract } from 'cooptypes';
|
||||
import type { BlockchainAccountInterface } from '~/types/shared';
|
||||
import type { MonoAccountDomainInterface } from '../interfaces/mono-account-domain.interface';
|
||||
|
||||
export class AccountDomainEntity {
|
||||
public readonly username!: string;
|
||||
public blockchain_account!: BlockchainAccountInterface | null;
|
||||
public user_account!: RegistratorContract.Tables.Accounts.IAccount | null;
|
||||
public provider_account!: MonoAccountDomainInterface | null;
|
||||
public participant_account!: SovietContract.Tables.Participants.IParticipants | null;
|
||||
// public cardcoop_account!: ?
|
||||
|
||||
constructor(data: AccountDomainEntity) {
|
||||
Object.assign(this, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { AccountDomainEntity } from '../entities/account-domain.entity';
|
||||
import config from '~/config/config';
|
||||
import { AccountDomainService } from '~/domain/account/services/account-domain.service';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { tokenService, userService } from '~/services';
|
||||
import type { MonoAccountDomainInterface } from '../interfaces/mono-account-domain.interface';
|
||||
import type { GetAccountsInputDomainInterface } from '../interfaces/get-accounts-input.interface';
|
||||
import type {
|
||||
PaginationInputDomainInterface,
|
||||
PaginationResultDomainInterface,
|
||||
} from '~/domain/common/interfaces/pagination.interface';
|
||||
import type { QueryResultLegacy } from '~/domain/common/interfaces/query-result-legacy-domain.interface';
|
||||
import type { RegisterAccountDomainInterface } from '../interfaces/register-account-input.interface';
|
||||
import type { RegisteredAccountDomainInterface } from '../interfaces/registeted-account.interface';
|
||||
import type { UpdateAccountDomainInterface } from '../interfaces/update-account-input.interface';
|
||||
import { AccountType } from '~/modules/account/enum/account-type.enum';
|
||||
import { ORGANIZATION_REPOSITORY, OrganizationRepository } from '~/domain/common/repositories/organization.repository';
|
||||
import { INDIVIDUAL_REPOSITORY, IndividualRepository } from '~/domain/common/repositories/individual.repository';
|
||||
import { ENTREPRENEUR_REPOSITORY, EntrepreneurRepository } from '~/domain/common/repositories/entrepreneur.repository';
|
||||
import { IndividualDomainEntity } from '~/domain/branch/entities/individual-domain.entity';
|
||||
import { OrganizationDomainEntity } from '~/domain/branch/entities/organization-domain.entity';
|
||||
import { EntrepreneurDomainEntity } from '~/domain/branch/entities/entrepreneur-domain.entity';
|
||||
|
||||
@Injectable()
|
||||
export class AccountDomainInteractor {
|
||||
constructor(
|
||||
private readonly accountDomainService: AccountDomainService,
|
||||
@Inject(ORGANIZATION_REPOSITORY) private readonly organizationRepository: OrganizationRepository,
|
||||
@Inject(INDIVIDUAL_REPOSITORY) private readonly individualRepository: IndividualRepository,
|
||||
@Inject(ENTREPRENEUR_REPOSITORY) private readonly entrepreneurRepository: EntrepreneurRepository
|
||||
) {}
|
||||
|
||||
async updateAccount(data: UpdateAccountDomainInterface): Promise<AccountDomainEntity> {
|
||||
await userService.updateUserByUsername(data.username, {
|
||||
email: data.email,
|
||||
});
|
||||
|
||||
// обновляем данные в хранилище
|
||||
if (data.type === AccountType.Individual && data.individual_data) {
|
||||
//здесь и далее мы подставляем email и username т.к. они требуются в интерфесе генератора
|
||||
const individual = new IndividualDomainEntity({ ...data.individual_data, username: data.username, email: data.email });
|
||||
await this.individualRepository.create(individual);
|
||||
} else if (data.type === AccountType.Organization && data.organization_data) {
|
||||
const organization = new OrganizationDomainEntity({
|
||||
...data.organization_data,
|
||||
username: data.username,
|
||||
email: data.email,
|
||||
});
|
||||
await this.organizationRepository.create(organization);
|
||||
} else if (data.type === AccountType.Entrepreneur && data.entrepreneur_data) {
|
||||
const entrepreneur = new EntrepreneurDomainEntity({
|
||||
...data.entrepreneur_data,
|
||||
username: data.username,
|
||||
email: data.email,
|
||||
});
|
||||
await this.entrepreneurRepository.create(entrepreneur);
|
||||
}
|
||||
|
||||
return await this.getAccount(data.username);
|
||||
}
|
||||
|
||||
async deleteAccount(username: string): Promise<void> {
|
||||
await userService.deleteUserByUsername(username);
|
||||
}
|
||||
|
||||
async registerAccount(data: RegisterAccountDomainInterface): Promise<RegisteredAccountDomainInterface> {
|
||||
//TODO refactor after migrate from mongo
|
||||
const user = await userService.createUser({ ...data, role: 'user' });
|
||||
const tokens = await tokenService.generateAuthTokens(user);
|
||||
|
||||
const account = await this.getAccount(data.username);
|
||||
|
||||
const result: RegisteredAccountDomainInterface = {
|
||||
account,
|
||||
tokens,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async getAccount(username: string): Promise<AccountDomainEntity> {
|
||||
return await this.accountDomainService.getAccount(username);
|
||||
}
|
||||
|
||||
async getAccounts(
|
||||
data: GetAccountsInputDomainInterface,
|
||||
options: PaginationInputDomainInterface
|
||||
): Promise<PaginationResultDomainInterface<AccountDomainEntity>> {
|
||||
const provider_accounts = (await userService.queryUsers(data, options)) as QueryResultLegacy<MonoAccountDomainInterface>;
|
||||
|
||||
const result: PaginationResultDomainInterface<AccountDomainEntity> = {
|
||||
items: [],
|
||||
totalCount: provider_accounts.totalResults,
|
||||
totalPages: provider_accounts.totalPages,
|
||||
currentPage: provider_accounts.page,
|
||||
};
|
||||
|
||||
for (const account of provider_accounts.results) {
|
||||
const user_account = await this.accountDomainService.getUserAccount(account.username);
|
||||
const blockchain_account = await this.accountDomainService.getBlockchainAccount(account.username);
|
||||
const participant_account = await this.accountDomainService.getParticipantAccount(config.coopname, account.username);
|
||||
|
||||
const item = new AccountDomainEntity({
|
||||
username: account.username,
|
||||
user_account,
|
||||
blockchain_account,
|
||||
provider_account: account,
|
||||
participant_account,
|
||||
});
|
||||
|
||||
result.items.push(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { RegistratorContract, SovietContract } from 'cooptypes';
|
||||
import type { BlockchainAccountInterface } from '~/types/shared';
|
||||
|
||||
export interface AccountBlockchainPort {
|
||||
getBlockchainAccount(username: string): Promise<BlockchainAccountInterface | null>;
|
||||
getCooperatorAccount(coopname: string): Promise<RegistratorContract.Tables.Cooperatives.ICooperative | null>;
|
||||
getParticipantAccount(
|
||||
coopname: string,
|
||||
username: string
|
||||
): Promise<SovietContract.Tables.Participants.IParticipants | null>;
|
||||
getUserAccount(username: string): Promise<RegistratorContract.Tables.Accounts.IAccount | null>;
|
||||
addParticipantAccount(data: RegistratorContract.Actions.AddUser.IAddUser): Promise<void>;
|
||||
}
|
||||
|
||||
export const ACCOUNT_BLOCKCHAIN_PORT = Symbol('AccountBlockchainPort');
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import type { Cooperative } from 'cooptypes';
|
||||
|
||||
export type CreateOrganizationDataInputDomainInterface = Omit<Cooperative.Users.IOrganizationData, 'username'> & {
|
||||
bank_account: Cooperative.Payments.IBankAccount;
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface GetAccountsInputDomainInterface {
|
||||
role?: string;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
export enum MonoAccountStatusDomainInterface {
|
||||
'Created' = 'created',
|
||||
'Joined' = 'joined',
|
||||
'Payed' = 'payed',
|
||||
'Registered' = 'registered',
|
||||
'Active' = 'active',
|
||||
'Failed' = 'failed',
|
||||
'Refunded' = 'refunded',
|
||||
'Blocked' = 'blocked',
|
||||
}
|
||||
|
||||
export interface MonoAccountDomainInterface {
|
||||
username: string;
|
||||
status: MonoAccountStatusDomainInterface;
|
||||
message?: string;
|
||||
is_registered: boolean;
|
||||
has_account: boolean;
|
||||
type: 'individual' | 'entrepreneur' | 'organization';
|
||||
public_key: string;
|
||||
referer: string;
|
||||
email: string;
|
||||
role: string;
|
||||
is_email_verified: boolean;
|
||||
initial_order?: string;
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import type { Cooperative } from 'cooptypes';
|
||||
import type { CreateOrganizationDataInputDomainInterface } from './create-organization-data-input-domain.interface';
|
||||
|
||||
export interface RegisterAccountDomainInterface {
|
||||
email: string;
|
||||
entrepreneur_data?: Omit<Cooperative.Users.IEntrepreneurData, 'username'> & {
|
||||
bank_account: Cooperative.Payments.IBankAccount;
|
||||
};
|
||||
individual_data?: Omit<Cooperative.Users.IIndividualData, 'username'>;
|
||||
organization_data?: CreateOrganizationDataInputDomainInterface;
|
||||
public_key?: string;
|
||||
referer?: string;
|
||||
type: 'individual' | 'entrepreneur' | 'organization';
|
||||
username: string;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { AccountDomainEntity } from '../entities/account-domain.entity';
|
||||
import type { TokensDomainInterface } from './tokens-domain.interface';
|
||||
|
||||
export interface RegisteredAccountDomainInterface {
|
||||
account: AccountDomainEntity;
|
||||
tokens: TokensDomainInterface;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface TokenDomainInterface {
|
||||
token: string;
|
||||
expires: Date;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { TokenDomainInterface } from './token-domain.interface';
|
||||
|
||||
export interface TokensDomainInterface {
|
||||
access: TokenDomainInterface;
|
||||
refresh: TokenDomainInterface;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { Cooperative } from 'cooptypes';
|
||||
import type { OrganizationType } from '~/modules/account/enum/organization-type.enum';
|
||||
|
||||
export interface UpdateAccountDomainInterface {
|
||||
email: string;
|
||||
entrepreneur_data?: Omit<Cooperative.Users.IEntrepreneurData, 'username'>;
|
||||
individual_data?: Omit<Cooperative.Users.IIndividualData, 'username'>;
|
||||
organization_data?: Omit<Cooperative.Users.IOrganizationData, 'username' | 'type'> & { type: OrganizationType };
|
||||
public_key?: string;
|
||||
referer?: string;
|
||||
role: 'user';
|
||||
type: 'individual' | 'entrepreneur' | 'organization';
|
||||
username: string;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||
import type { BlockchainAccountInterface } from '~/types/shared';
|
||||
import { ACCOUNT_BLOCKCHAIN_PORT, type AccountBlockchainPort } from '../interfaces/account-blockchain.port';
|
||||
import type { RegistratorContract, SovietContract } from 'cooptypes';
|
||||
import config from '~/config/config';
|
||||
import { AccountDomainEntity } from '../entities/account-domain.entity';
|
||||
import type { MonoAccountDomainInterface } from '../interfaces/mono-account-domain.interface';
|
||||
import { userService } from '~/services';
|
||||
import type { RegisterAccountDomainInterface } from '../interfaces/register-account-input.interface';
|
||||
import { userStatus } from '~/types';
|
||||
|
||||
@Injectable()
|
||||
export class AccountDomainService {
|
||||
constructor(@Inject(ACCOUNT_BLOCKCHAIN_PORT) private readonly accountBlockchainPort: AccountBlockchainPort) {}
|
||||
|
||||
async addProviderAccount(data: RegisterAccountDomainInterface): Promise<MonoAccountDomainInterface> {
|
||||
//TODO refactor it after migrate from mongo
|
||||
const user = await userService.createUser({ ...data, role: 'user' });
|
||||
user.status = userStatus['4_Registered'];
|
||||
user.is_registered = true;
|
||||
user.has_account = true;
|
||||
|
||||
await user.save();
|
||||
return user as unknown as MonoAccountDomainInterface;
|
||||
}
|
||||
|
||||
async addParticipantAccount(data: RegistratorContract.Actions.AddUser.IAddUser): Promise<void> {
|
||||
try {
|
||||
await this.accountBlockchainPort.addParticipantAccount(data);
|
||||
} catch (e: any) {
|
||||
// удаляем аккаунт провайдера если транзакция в блокчейн не прошла (для возможности повтора)
|
||||
await userService.deleteUserByUsername(data.username);
|
||||
throw new BadRequestException(e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async getAccount(username: string): Promise<AccountDomainEntity> {
|
||||
const user_account = await this.getUserAccount(username);
|
||||
const blockchain_account = await this.getBlockchainAccount(username);
|
||||
const participant_account = await this.getParticipantAccount(config.coopname, username);
|
||||
|
||||
//TODO refactor after migrate from mongo
|
||||
const provider_account = (await userService.findUser(username)) as unknown as MonoAccountDomainInterface;
|
||||
|
||||
return new AccountDomainEntity({
|
||||
username,
|
||||
user_account,
|
||||
blockchain_account,
|
||||
provider_account: provider_account,
|
||||
participant_account,
|
||||
});
|
||||
}
|
||||
|
||||
async getBlockchainAccount(username: string): Promise<BlockchainAccountInterface | null> {
|
||||
return await this.accountBlockchainPort.getBlockchainAccount(username);
|
||||
}
|
||||
|
||||
async getCooperatorAccount(username: string): Promise<RegistratorContract.Tables.Cooperatives.ICooperative | null> {
|
||||
return await this.accountBlockchainPort.getCooperatorAccount(username);
|
||||
}
|
||||
|
||||
async getParticipantAccount(
|
||||
coopname: string,
|
||||
username: string
|
||||
): Promise<SovietContract.Tables.Participants.IParticipants | null> {
|
||||
return await this.accountBlockchainPort.getParticipantAccount(coopname, username);
|
||||
}
|
||||
|
||||
async getUserAccount(username: string): Promise<RegistratorContract.Tables.Accounts.IAccount | null> {
|
||||
return await this.accountBlockchainPort.getUserAccount(username);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DocumentDomainModule } from '../document/document.module';
|
||||
import { AgendaDomainInteractor } from './interactors/agenda-domain.interactor';
|
||||
|
||||
@Module({
|
||||
imports: [DocumentDomainModule],
|
||||
providers: [AgendaDomainInteractor],
|
||||
exports: [AgendaDomainInteractor],
|
||||
})
|
||||
export class AgendaDomainModule {}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { DocumentDomainService } from '~/domain/document/services/document-domain.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { blockchainService } from '~/services';
|
||||
import config from '~/config/config';
|
||||
import { SovietContract } from 'cooptypes';
|
||||
import type { AgendaWithDocumentsDomainInterface } from '../interfaces/agenda-with-documents-domain.interface';
|
||||
import { getActions } from '~/utils/getFetch';
|
||||
import type { VotingAgendaDomainInterface } from '../interfaces/voting-agenda-domain.interface';
|
||||
|
||||
@Injectable()
|
||||
export class AgendaDomainInteractor {
|
||||
constructor(private readonly documentDomainService: DocumentDomainService) {}
|
||||
|
||||
async getAgenda(): Promise<AgendaWithDocumentsDomainInterface[]> {
|
||||
// Шаг 1: Загрузить повестку дня
|
||||
const agenda = await this.loadAgenda(config.coopname);
|
||||
|
||||
// Шаг 2: Построить комплексную повестку (complexAgenda)
|
||||
const complexAgenda: AgendaWithDocumentsDomainInterface[] = [];
|
||||
|
||||
for (const { action, table } of agenda) {
|
||||
// Создание пакета документов для каждого действия
|
||||
const documents = await this.documentDomainService.buildDocumentPackage(action);
|
||||
|
||||
// Проверяем наличие заявления, прежде чем добавлять в комплексную повестку
|
||||
// Делаем только потому что в локальной разработке заявлений после перезапуска блокчейна может и не быть в истории цепочки,
|
||||
// а это ломает отображение на фронте. При нормальные условиях заявление всегда должно быть.
|
||||
if (documents.statement?.document) {
|
||||
complexAgenda.push({
|
||||
table,
|
||||
action,
|
||||
documents,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Шаг 3: Вернуть итоговую комплексную повестку
|
||||
return complexAgenda;
|
||||
}
|
||||
|
||||
/**
|
||||
* Загружает повестку дня (agenda) на основании решений и связанных действий.
|
||||
*/
|
||||
async loadAgenda(coopname: string): Promise<VotingAgendaDomainInterface[]> {
|
||||
//TODO блокчейн-адаптер здесь повесить
|
||||
const api = await blockchainService.getApi();
|
||||
|
||||
// Загружаем таблицу решений
|
||||
const decisions = (await blockchainService.lazyFetch(
|
||||
api,
|
||||
SovietContract.contractName.production as string,
|
||||
coopname,
|
||||
'decisions'
|
||||
)) as SovietContract.Tables.Decisions.IDecision[];
|
||||
|
||||
const agenda: VotingAgendaDomainInterface[] = [];
|
||||
|
||||
for (const decision of decisions) {
|
||||
// Ищем экшен, связанный с конкретным решением
|
||||
const actionResponse = await getActions(`${process.env.SIMPLE_EXPLORER_API}/get-actions`, {
|
||||
filter: JSON.stringify({
|
||||
account: SovietContract.contractName.production,
|
||||
name: SovietContract.Actions.Registry.NewSubmitted.actionName,
|
||||
receiver: process.env.COOPNAME,
|
||||
'data.decision_id': String(decision.id),
|
||||
}),
|
||||
page: 1,
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
const action = actionResponse?.results?.[0];
|
||||
if (action) {
|
||||
agenda.push({
|
||||
table: decision,
|
||||
action,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return agenda;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { GeneratedDocumentDomainInterface } from '~/domain/document/interfaces/generated-document-domain.interface';
|
||||
import type { ExtendedBlockchainActionDomainInterface } from './extended-blockchain-action-domain.interface';
|
||||
|
||||
export interface ActDetailDomainInterface {
|
||||
action?: ExtendedBlockchainActionDomainInterface;
|
||||
document?: GeneratedDocumentDomainInterface;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import type { DocumentPackageDomainInterface } from './document-package-domain.interface';
|
||||
import type { VotingAgendaDomainInterface } from './voting-agenda-domain.interface';
|
||||
|
||||
export interface AgendaWithDocumentsDomainInterface extends VotingAgendaDomainInterface {
|
||||
documents: DocumentPackageDomainInterface;
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type { Cooperative } from 'cooptypes';
|
||||
|
||||
export type BlockchainActionDomainInterface = Cooperative.Blockchain.IAction;
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { GeneratedDocumentDomainInterface } from '~/domain/document/interfaces/generated-document-domain.interface';
|
||||
import type { ExtendedBlockchainActionDomainInterface } from './extended-blockchain-action-domain.interface';
|
||||
|
||||
export interface DecisionDetailDomainInterface {
|
||||
action: ExtendedBlockchainActionDomainInterface;
|
||||
document: GeneratedDocumentDomainInterface;
|
||||
votes_for: ExtendedBlockchainActionDomainInterface[];
|
||||
votes_against: ExtendedBlockchainActionDomainInterface[];
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import type { GeneratedDocumentDomainInterface } from '~/domain/document/interfaces/generated-document-domain.interface';
|
||||
import type { ActDetailDomainInterface } from './act-detail-domain.interface';
|
||||
import type { DecisionDetailDomainInterface } from './decision-detail-domain.interface';
|
||||
import type { StatementDetailDomainInterface } from './statement-detail-domain.interface';
|
||||
|
||||
export interface DocumentPackageDomainInterface {
|
||||
statement: StatementDetailDomainInterface;
|
||||
decision: DecisionDetailDomainInterface;
|
||||
acts: ActDetailDomainInterface[];
|
||||
links: GeneratedDocumentDomainInterface[];
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import type { DocumentPackageDomainInterface } from './document-package-domain.interface';
|
||||
|
||||
export interface DocumentPackagesResponseDomainInterface {
|
||||
results: DocumentPackageDomainInterface[];
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user