Compare commits

...

268 Commits

Author SHA1 Message Date
Alex Ant 90e8c6b3c4 chore(release): publish 2024-10-30 19:49:43 +05:00
Alex Ant 07bf05cb2c normal plugin model with some payment plugins 2024-10-30 19:42:48 +05:00
Alex Ant 49d29902a8 update 2024-10-29 23:42:49 +05:00
Alex Ant 32a90c2a9e controller-next initialization 2024-10-29 23:42:14 +05:00
Alex Ant 16ed2c1517 support 2024-10-29 11:50:49 +05:00
Alex Ant 8bb0f1c799 chore(release): publish 2024-10-28 21:49:21 +05:00
Alex Ant f14fe2dd64 update cardcoop 2024-10-28 20:45:08 +05:00
Alex Ant 884f0ee0de changeAuthorizedHome 2024-10-28 19:47:55 +05:00
Alex Ant 744270f48c update 2024-10-28 19:42:04 +05:00
Alex Ant c30849be3c cardcoop on a main screen 2024-10-28 19:06:15 +05:00
Alex Ant f295ac8b4d separated settings page 2024-10-28 17:56:40 +05:00
Alex Ant 9469a8f43d update 2024-10-28 14:18:07 +05:00
Alex Ant 786799ca40 update 2024-10-28 14:16:53 +05:00
Alex Ant aa6238cf5a fix 2024-10-28 13:38:54 +05:00
Alex Ant 1ed31ee2e2 update 2024-10-28 13:26:42 +05:00
Alex Ant 7d8c12977c generateOptions for Documents 2024-10-28 13:15:07 +05:00
Alex Ant 81ee1d63a6 Merge branch 'main' into dev 2024-10-28 11:59:00 +05:00
Alex Ant cf1c1b3198 chore(release): publish 2024-10-28 11:55:43 +05:00
Alex Ant bc7b1e8472 update 2024-10-28 11:55:02 +05:00
Alex Ant 7319c41cc1 chore(release): publish 2024-10-28 11:51:09 +05:00
Alex Ant 2f09fc32c0 finish bugfix 2024-10-28 11:50:41 +05:00
Alex Ant f85b3f54a1 chore(release): publish 2024-10-28 10:49:06 +05:00
Alex Ant 1a68669d6d update Dockerfile 2024-10-28 10:48:04 +05:00
Alex Ant 6345c2d414 chore(release): publish 2024-10-28 10:16:08 +05:00
Alex Ant 87930cad75 update 2024-10-28 10:14:41 +05:00
Alex Ant 3ab2232121 Dockerfile 2024-10-28 10:06:40 +05:00
Alex Ant 9719e25421 move to weasyprint 2024-10-28 10:03:29 +05:00
Alex Ant 8e1f998c39 try to fix it ! 2024-10-27 20:34:08 +05:00
Alex Ant deb1c224f8 update 2024-10-27 19:33:06 +05:00
Alex Ant b2fa79898a arial embded font 2024-10-27 17:56:49 +05:00
Alex Ant da2759a3c6 update 2024-10-27 15:29:12 +05:00
Alex Ant f6bc696426 add common Arial font 2024-10-27 15:09:17 +05:00
Alex Ant da653e9b32 looking for bug #2 2024-10-27 12:46:47 +05:00
Alex Ant 227e153bde looking for bug 2024-10-27 12:03:18 +05:00
Alex Ant 12f906b9b0 update 2024-10-26 21:09:25 +05:00
Alex Ant c8236d4d10 update 2024-10-26 21:01:26 +05:00
Alex Ant e2006da50d TSK-289 Устранить избыточное сохранение документа при регенерации (1) 2024-10-26 20:20:05 +05:00
Alex Ant 433a7d3641 chore(release): publish 2024-10-26 17:45:45 +05:00
Alex Ant 6b41c74b43 regenerate document button, some bugs and fixs 2024-10-26 17:38:12 +05:00
Alex Ant cc3e7ff7c6 fix bug auto powerup on every restart 2024-10-26 13:42:11 +05:00
Alex Ant a35a6bf642 separate init payment factory 2024-10-26 13:10:52 +05:00
Alex Ant 9ecd523646 fix double subscriber bug 2024-10-26 12:52:24 +05:00
Alex Ant e7f896c38e update notification template 2024-10-26 10:49:28 +05:00
Alex Ant 624ec743b1 fix bug loading button 2024-10-25 19:26:41 +05:00
Alex Ant 553c5745ef fix update soviet bug 2024-10-25 16:46:26 +05:00
Alex Ant 3f2779ca3d fix bug voskhod everywhere 2024-10-25 16:42:18 +05:00
Alex Ant e51b962627 databazor bug fix 2024-10-25 16:20:58 +05:00
Alex Ant f709e135b6 Merge branch 'testnet' 2024-10-25 11:10:33 +05:00
Alex Ant 50b48aa247 update 2024-10-24 11:44:05 +05:00
Alex Ant 508f2abc82 update 2024-10-24 11:37:39 +05:00
Alex Ant 42ec56e578 fix 2024-10-24 11:26:07 +05:00
Alex Ant 92673dac6e chore(release): publish 2024-10-23 21:21:41 +05:00
Alex Ant daf57aba35 fix 2024-10-23 21:21:00 +05:00
Alex Ant dee629c429 chore(release): publish 2024-10-23 21:16:50 +05:00
Alex Ant 67f9092350 fix lib 2024-10-23 21:15:52 +05:00
Alex Ant 88691d47a5 chore(release): publish 2024-10-23 21:02:47 +05:00
Alex Ant 0fdd9bc803 update 2024-10-23 21:02:25 +05:00
Alex Ant 8452c607b4 chore(release): publish 2024-10-23 20:57:27 +05:00
Alex Ant 4fc57c2d57 update 2024-10-23 20:56:58 +05:00
Alex Ant 9f0b707a30 chore(release): publish 2024-10-23 20:51:30 +05:00
Alex Ant 511bc351d5 publish 2024-10-23 20:49:38 +05:00
Alex Ant 56ee3e962c chore(release): publish 2024-10-23 20:42:13 +05:00
Alex Ant 2781e8519d fix 2024-10-23 20:40:34 +05:00
Alex Ant 46575348fe chore(release): publish 2024-10-23 20:24:23 +05:00
Alex Ant bae2ca646b publish again 2024-10-23 20:24:00 +05:00
Alex Ant 0686c6ab00 chore(release): publish 2024-10-23 20:20:51 +05:00
Alex Ant db04b66ce5 update 2024-10-23 20:20:25 +05:00
Alex Ant c43c8a29af chore(release): publish 2024-10-23 20:17:40 +05:00
Alex Ant aaea18abe1 publish 2024-10-23 20:16:35 +05:00
Alex Ant 0b6076cb61 chore(release): publish 2024-10-23 20:10:05 +05:00
Alex Ant 3b6d02f02c fix 2024-10-23 20:08:45 +05:00
Alex Ant 625a0586a3 public 2024-10-23 20:06:22 +05:00
Alex Ant 10d63ca586 powerup is private plugin 2024-10-23 20:06:01 +05:00
Alex Ant d2697bf9de make library from terminal 2024-10-23 20:04:34 +05:00
Alex Ant fa8c8843e2 make powerup private plugin 2024-10-19 22:07:20 +05:00
Alex Ant 7b59ea4b96 chore(release): publish 2024-10-19 22:03:06 +05:00
Alex Ant 058215934e Merge branch 'testnet' 2024-10-19 22:02:08 +05:00
Alex Ant 1d724d841f plugin factory and powerup plugin 2024-10-19 21:58:34 +05:00
Alex Ant 32db2100de implementation plugins worker system 2024-10-18 11:27:39 +05:00
Alex Ant 095c77b65b chore(release): publish 2024-10-17 16:20:02 +05:00
Alex Ant c1c07bff8f plugin factory and powerup plugin sketch 2024-10-17 16:18:39 +05:00
Alex Ant 1206bd0e98 fix voskhod everywhere bug 2024-10-17 13:22:35 +05:00
Alex Ant 67bcd1b307 update text on install 2024-10-17 10:52:06 +05:00
Alex Ant 31ef70e433 health check 2024-10-16 13:56:37 +05:00
Alex Ant afb12961d2 fix launch errors 2024-10-16 13:38:08 +05:00
Alex Ant 3dbd40e334 bug fix soviet update 2024-10-16 11:10:18 +05:00
Alex Ant bc5d6e38c8 console.error(e 2024-10-15 23:37:27 +05:00
Alex Ant 5a39143051 openReplay connection 2024-10-14 21:24:45 +05:00
Alex Ant 18e9571877 fix css bug 2024-10-13 16:52:55 +05:00
Alex Ant a828f4c831 chore(release): publish 2024-10-13 16:26:37 +05:00
Alex Ant 54ac12f250 update migrations 2024-10-13 16:24:26 +05:00
Alex Ant 9a6a72f605 feat: запрос соглашений
fix: интерфейс
2024-10-13 16:13:52 +05:00
Alex Ant 03949489e8 docs: добавлен файл конвенции коммитов 2024-10-13 13:19:16 +05:00
Alex Ant 8ebd735e7e update 2024-10-13 13:08:59 +05:00
Alex Ant 39246da73f update amount in agreement and fix bug in terminal 2024-10-13 12:00:39 +05:00
Alex Ant bf33f60368 cooperative connection 2024-10-13 11:50:22 +05:00
Alex Ant 492a613769 fix bugs 2024-10-12 00:59:58 +05:00
Alex Ant 2c02c3434e update 2024-10-12 00:51:37 +05:00
Alex Ant a186ddd9d2 chore(release): publish 2024-10-12 00:46:06 +05:00
Alex Ant 4b0c7a5e4e update 2024-10-12 00:45:35 +05:00
Alex Ant 54cfbe3ab6 fix 2024-10-12 00:39:07 +05:00
Alex Ant 02015cf0b8 update migrator package.json 2024-10-12 00:37:39 +05:00
Alex Ant 416e4d3598 interface rework & documents style change 2024-10-12 00:31:52 +05:00
Alex Ant e0d9c4b0ea make migrator private 2024-10-10 20:55:46 +05:00
Alex Ant 2659e3bc09 migrator release 2024-10-10 20:54:27 +05:00
Alex Ant d296167b6a agreementChecker fix, payment description fix, loader on add user 2024-10-10 20:41:18 +05:00
Alex Ant 89b0aa7fec chore(release): publish 2024-10-10 18:38:19 +05:00
Alex Ant 787aadf444 fix bugs 2024-10-10 18:09:22 +05:00
Alex Ant d67161a29b slice secret 2024-10-10 15:02:12 +05:00
Alex Ant 7f4777ead4 правка текста вступительного взноса 2024-10-10 14:41:26 +05:00
Alex Ant 1e2147b143 fix aggregation 2 2024-10-10 12:09:10 +05:00
Alex Ant 9f0a2fbf63 chore(release): publish 2024-10-10 11:28:11 +05:00
Alex Ant 7804851bc9 update agregate pipeline 2024-10-10 11:15:47 +05:00
Alex Ant adc50dd89e chore(release): publish 2024-10-09 22:20:55 +05:00
Alex Ant 5378cf5ddc template fix 2024-10-09 22:14:11 +05:00
Alex Ant a733bb605f fix model 2024-10-09 14:17:19 +05:00
Alex Ant aaa1b5a264 test update 2024-10-09 14:01:21 +05:00
Alex Ant 5855cdee65 install process 2024-10-09 13:46:08 +05:00
Alex Ant c0d584e644 TSK-225 vaultModel and wif store for cooperative operations 2024-10-04 12:01:44 +05:00
Alex Ant 955f0f473e fix bug 2024-09-30 23:10:59 +05:00
Alex Ant 9ceb888124 update 2024-09-30 22:13:59 +05:00
Alex Ant e902933693 update 2024-09-30 21:43:41 +05:00
Alex Ant e07b47fb10 update 2024-09-30 21:37:54 +05:00
Alex Ant 5bf08d9701 update 2024-09-30 21:33:02 +05:00
Alex Ant 597fa1e6fa update package 2024-09-30 21:21:45 +05:00
Alex Ant dda3ec0ba0 chore(release): publish 2024-09-30 21:09:57 +05:00
Alex Ant 991f08c749 dompurify and coopback <br> fix 2024-09-30 21:08:26 +05:00
Alex Ant 923865cd7c bug fix #3 2024-09-30 20:00:25 +05:00
Alex Ant 606aee8ccd chore(release): publish 2024-09-30 17:24:04 +05:00
Alex Ant 4a1cb27d0a big bug #2 2024-09-30 17:21:38 +05:00
Alex Ant 26cd7f3a12 chore(release): publish 2024-09-30 17:09:39 +05:00
Alex Ant f4045dbc56 chore(release): publish 2024-09-30 17:07:54 +05:00
Alex Ant 33c2b24550 fix bug of parallel generation 2024-09-30 17:06:17 +05:00
Alex Ant cd86f3c8ea redis auth 2024-09-30 15:15:57 +05:00
Alex Ant 58026ebc2d fix redis auth 2024-09-30 15:07:33 +05:00
Alex Ant 9183275dad redis authentification 2024-09-30 15:00:38 +05:00
Alex Ant 5e3a625d95 chore(release): publish 2024-09-30 14:52:28 +05:00
Alex Ant 3297f9d2d2 update package.json 2024-09-30 14:51:18 +05:00
Alex Ant 29b8306a28 chore(release): publish 2024-09-29 19:17:36 +05:00
Alex Ant d84746bac6 add compression package 2024-09-29 19:14:12 +05:00
Alex Ant 4eacf2ff0b chore(release): publish 2024-09-29 19:01:29 +05:00
Alex Ant a68e0d6f61 update 2024-09-29 18:57:09 +05:00
Alex Ant ddf44304c7 order_id -> order_num, stable initial_order 2024-09-29 15:28:16 +05:00
Alex Ant 789d98f58e chore(release): publish 2024-09-29 09:39:57 +05:00
Alex Ant 205574620d Merge branch 'testnet' 2024-09-29 09:39:19 +05:00
Alex Ant d9bae12b31 chore(release): publish 2024-09-29 09:36:26 +05:00
Alex Ant c951190794 update 2024-09-29 09:34:32 +05:00
Alex Ant d4e1d69c3c update 2024-09-29 09:29:53 +05:00
Alex Ant f93a32c07b update 2024-09-29 09:25:10 +05:00
Alex Ant 8e7c152c0d update 2024-09-29 09:23:20 +05:00
Alex Ant 6a64404f20 update 2024-09-29 09:19:43 +05:00
Alex Ant 1a09589add from-packages 2024-09-29 09:18:03 +05:00
Alex Ant d0f9072ab5 fixed packet version 2024-09-29 09:15:58 +05:00
Alex Ant 011ed9f2d8 update 2024-09-29 09:11:57 +05:00
Alex Ant 12332f83d9 Merge branch 'dev' into testnet 2024-09-29 09:05:09 +05:00
Alex Ant 8f75829948 update 2024-09-29 09:04:53 +05:00
Alex Ant a7ffedb3b2 update 2024-09-29 09:03:14 +05:00
Alex Ant 378d3b9747 update 2024-09-29 09:02:06 +05:00
Alex Ant 5ba05409c4 chore(release): publish
- coopback@1.7.38-alpha.3
 - coopdoc-generator-ts@1.0.21-alpha.3
 - cooptypes@1.0.26-alpha.3
 - coop-notificator@0.0.29-alpha.3
 - terminal@0.1.51-alpha.3
2024-09-29 08:58:35 +05:00
Alex Ant d86beef58f document updates 2024-09-29 08:57:37 +05:00
Alex Ant 7315d67c9f chore(release): publish
- coopback@1.7.38-alpha.2
 - coopdoc-generator-ts@1.0.21-alpha.2
 - cooptypes@1.0.26-alpha.2
 - coop-notificator@0.0.29-alpha.2
 - terminal@0.1.51-alpha.2
2024-09-28 19:43:01 +05:00
Alex Ant 59d79b8c7a update order statuses, list order statuses, add passport data, update registry documents 2024-09-28 19:41:12 +05:00
Alex Ant 3df97653a2 TSK-181 StatusPage and setStatus for orders [pre] 2024-09-26 21:30:08 +05:00
Alex Ant 948d55e418 download qr and show order data 2024-09-23 21:18:10 +05:00
Alex Ant 1d2c7281b1 sberbankPollingProvider prototype 2024-09-23 12:46:12 +05:00
Alex Ant 35759b943c chore(release): publish
- coopback@1.7.38-alpha.1
 - coopdoc-generator-ts@1.0.21-alpha.1
 - cooptypes@1.0.26-alpha.1
 - coop-notificator@0.0.29-alpha.1
 - terminal@0.1.51-alpha.1
2024-09-20 12:13:47 +05:00
Alex Ant 29da33e200 update 2024-09-20 12:12:06 +05:00
Alex Ant 1d758f8126 gitignore 2024-09-20 10:09:54 +05:00
Alex Ant b663dac2ab delete logs 2024-09-20 10:09:32 +05:00
Alex Ant 485a3971e9 update 2024-09-20 10:09:13 +05:00
Alex Ant 36b5d64ca6 chore(release): publish
- coopback@1.7.38-alpha.0
 - coopdoc-generator-ts@1.0.21-alpha.0
 - cooptypes@1.0.26-alpha.0
 - coop-notificator@0.0.29-alpha.0
 - terminal@0.1.51-alpha.0
2024-09-16 12:14:29 +05:00
Alex Ant 8462a709bc hz 2024-09-16 12:13:44 +05:00
Alex Ant 20cabe8362 update 2024-09-16 12:00:54 +05:00
Alex Ant 6967864361 chore(release): publish
- coopback@1.7.37-alpha.3
 - coopdoc-generator-ts@1.0.11-alpha.2
 - terminal@0.1.41-alpha.2
2024-09-16 11:58:50 +05:00
Alex Ant a6ee3840ae update 2024-09-16 11:57:43 +05:00
Alex Ant 01de6139df update 2024-09-16 11:46:48 +05:00
Alex Ant 6ba0924f6c chore(release): publish
- coopback@1.7.37-alpha.2
 - coopdoc-generator-ts@1.0.11-alpha.2
 - cooptypes@1.0.22-alpha.6
 - coop-notificator@0.0.28-alpha.1
 - terminal@0.1.41-alpha.1
2024-09-16 11:44:22 +05:00
Alex Ant 00c75d1fd8 update 2024-09-16 11:43:37 +05:00
Alex Ant 93f28fa9d5 update 2024-09-16 11:38:25 +05:00
Alex Ant 0aed10eb8b chore(release): publish
- coopback@1.7.37-alpha.1
 - coopdoc-generator-ts@1.0.11-alpha.1
 - cooptypes@1.0.22-alpha.6
 - coop-notificator@0.0.28-alpha.0
 - terminal@0.1.41-alpha.0
2024-09-16 11:37:13 +05:00
Alex Ant d84d687eee update 2024-09-16 11:36:50 +05:00
Alex Ant d046f56832 update 2024-09-16 11:34:07 +05:00
Alex Ant 0af1fbf313 update 2024-09-16 11:24:50 +05:00
Alex Ant e9aaf4309e chore(release): publish
- cooparser-ts@1.0.20-alpha.0
 - coopback@1.7.37-alpha.0
 - coopdoc-generator-ts@1.0.11-alpha.0
 - cooptypes@1.0.22-alpha.6
 - coop-notificator@0.0.27
 - terminal@0.1.40
2024-09-16 11:19:45 +05:00
Alex Ant 80fa1085d2 FUCK 2024-09-16 11:13:22 +05:00
Alex Ant 88195291fd update 2024-09-16 11:09:40 +05:00
Alex Ant 39ad600d00 update 2024-09-16 10:58:59 +05:00
Alex Ant c63a3aba6b update 2024-09-16 10:41:15 +05:00
Alex Ant 782ecbe307 dfdf 2024-09-16 10:36:54 +05:00
Alex Ant f75bbfa048 fix 2024-09-16 10:15:16 +05:00
Alex Ant 3a772cc983 herna 2024-09-16 10:00:29 +05:00
Alex Ant 807113523e documents update 2024-09-16 09:52:00 +05:00
Alex Ant 7df2af8611 update 2024-09-15 17:28:33 +05:00
Alex Ant 10c1697b00 update 2024-09-15 17:23:58 +05:00
Alex Ant 742e5d38ab chore(release): publish
- coopback@1.7.35-alpha.5
 - coopdoc-generator-ts@1.0.79-alpha.5
 - cooptypes@1.0.22-alpha.5
 - coop-notificator@0.0.26-alpha.5
 - terminal@0.1.39-alpha.5
2024-09-15 17:20:44 +05:00
Alex Ant 1d3e7f8af0 update 2024-09-15 17:19:13 +05:00
Alex Ant c99bacab64 before update 2024-09-15 10:40:18 +05:00
Alex Ant 95d7555ebc ssr & covars -> vars 2024-09-15 09:38:02 +05:00
Alex Ant 5854e0a23c chore(release): publish
- coopback@1.7.35-alpha.4
 - coopdoc-generator-ts@1.0.79-alpha.4
 - cooptypes@1.0.22-alpha.4
 - coop-notificator@0.0.26-alpha.4
 - terminal@0.1.39-alpha.4
2024-09-14 17:02:55 +05:00
Alex Ant 81f7f32767 fix 2024-09-14 17:02:33 +05:00
Alex Ant c0374a7da1 chore(release): publish
- coopback@1.7.35-alpha.3
 - coopdoc-generator-ts@1.0.79-alpha.3
 - cooptypes@1.0.22-alpha.3
 - coop-notificator@0.0.26-alpha.3
 - terminal@0.1.39-alpha.3
2024-09-14 16:44:32 +05:00
Alex Ant ed65e655b9 update 2024-09-14 16:43:06 +05:00
Alex Ant d250e516b5 update document models 2024-09-14 12:43:06 +05:00
Alex Ant e4f404b2c7 documents update 2024-09-13 20:41:00 +05:00
Alex Ant 86571331f8 update registry 2024-09-12 15:05:11 +05:00
Alex Ant 163d420af9 request agreements 2024-09-11 16:12:05 +05:00
Alex Ant 4925ea4af0 chore(release): publish
- cooparser-ts@1.0.19-alpha.1
 - coopback@1.7.35-alpha.2
 - coopdoc-generator-ts@1.0.79-alpha.2
 - cooptypes@1.0.22-alpha.2
 - coop-notificator@0.0.26-alpha.2
 - terminal@0.1.39-alpha.2
2024-09-11 10:04:46 +05:00
Alex Ant 57b05ce348 registration agreements 2024-09-11 10:01:09 +05:00
Alex Ant 427b53fcbd chore(release): publish
- coopback@1.7.35-alpha.1
 - coopdoc-generator-ts@1.0.79-alpha.1
 - cooptypes@1.0.22-alpha.1
 - coop-notificator@0.0.26-alpha.1
 - terminal@0.1.39-alpha.1
2024-09-10 10:06:12 +05:00
Alex Ant 2ac7a31be6 промежуточный результат добавления кооперативных соглашений 2024-09-10 10:05:17 +05:00
Alex Ant 4baed6409f chore(release): publish
- cooparser-ts@1.0.19-alpha.0
 - coopback@1.7.35-alpha.0
 - coopdoc-generator-ts@1.0.79-alpha.0
 - cooptypes@1.0.22-alpha.0
 - coop-notificator@0.0.26-alpha.0
 - terminal@0.1.39-alpha.0
2024-09-06 16:39:34 +05:00
Alex Ant 2ea0ff0965 big update 2024-09-06 16:29:18 +05:00
Alex Ant 88b9a7edce small letters 2024-09-05 19:27:23 +05:00
Alex Ant 538066b960 ufff template or Template ??? always should be small letters 2024-09-05 19:25:37 +05:00
Alex Ant b10dd1d75a update generator with new type of document 2024-09-04 20:57:59 +05:00
Alex Ant f743580b55 fix signature collection name 2024-09-03 20:33:30 +05:00
Alex Ant 0688467302 update 2024-09-03 20:11:17 +05:00
Alex Ant 29e972dbf2 fix is_paid 2024-08-27 18:47:39 +05:00
Alex Ant bd99697c5b chore(release): publish
- coopback@1.7.34
 - coopdoc-generator-ts@1.0.78
 - cooptypes@1.0.21
 - coop-notificator@0.0.25
 - terminal@0.1.38
2024-08-25 11:39:35 +05:00
Alex Ant 1d5c431ff6 update types 2024-08-25 11:38:40 +05:00
Alex Ant 3736c510ff chore(release): publish
- cooparser-ts@1.0.18
 - coopback@1.7.33
 - coopdoc-generator-ts@1.0.77
 - terminal@0.1.37
2024-08-25 11:27:44 +05:00
Alex Ant 26c12c3a85 update 2024-08-25 11:25:28 +05:00
Alex Ant 10bb08e5e6 chore(release): publish
- coopback@1.7.32
 - coopdoc-generator-ts@1.0.76
 - terminal@0.1.36
2024-08-25 11:19:13 +05:00
Alex Ant b50d5b6121 publish 2024-08-25 11:07:57 +05:00
Alex Ant 38b28752e1 chore(release): publish
- coopback@1.7.31
 - coopdoc-generator-ts@1.0.75
 - cooptypes@1.0.20
 - coop-notificator@0.0.24
 - terminal@0.1.35
2024-08-25 11:02:44 +05:00
Alex Ant 87c51009de ProgramProvision | ЦПП Цифровой Кошелёк 2024-08-25 10:59:57 +05:00
Alex Ant a950a47b28 chore(release): publish
- coopback@1.7.31-alpha.1
 - coopdoc-generator-ts@1.0.75-alpha.1
 - cooptypes@1.0.20-alpha.1
 - coop-notificator@0.0.24-alpha.1
 - terminal@0.1.35-alpha.1
2024-08-23 10:38:04 +05:00
Alex Ant 2609d10c0b update 2024-08-23 10:35:48 +05:00
Alex Ant 7b4cb0f66c publish 2024-08-23 10:34:38 +05:00
Alex Ant 26db0e1e69 push 2024-08-21 15:57:02 +05:00
Alex Ant 62e64cbd22 upgrade 2024-08-21 14:43:43 +05:00
Alex Ant 844a47830f fix types 2024-08-21 13:11:37 +05:00
Alex Ant 2cc1e32f45 chore(release): publish
- coopback@1.7.31-alpha.0
 - coopdoc-generator-ts@1.0.75-alpha.0
 - cooptypes@1.0.20-alpha.0
 - coop-notificator@0.0.24-alpha.0
 - terminal@0.1.35-alpha.0
2024-08-21 12:53:38 +05:00
Alex Ant 9890958b1e update contracts interface 2024-08-21 12:50:44 +05:00
Alex Ant 6dbd6d6edc fix 2024-08-19 14:31:01 +05:00
Alex Ant bf7a4dfd28 путь установки 2024-08-19 14:20:38 +05:00
Alex Ant f52626bda0 Полное разделение форм ввода данных пользователя пригодное для использования в процессе инсталляции 2024-08-16 16:17:24 +05:00
Alex Ant 88cec4bf2b рефакторинг | вынос форм для ввода пользовательской информации в shared для дальнейшего использования в модуле установки 2024-08-15 20:30:01 +05:00
Alex Ant b880def108 fix 2024-08-15 12:39:19 +05:00
Alex Ant bfd5dc039a addUser в панели управления 2024-08-15 12:28:18 +05:00
Alex Ant c0d236dff3 chore(release): publish
- coopback@1.7.30
 - coopdoc-generator-ts@1.0.74
 - cooptypes@1.0.19
 - coop-notificator@0.0.23
 - terminal@0.1.34
2024-08-13 23:03:13 +05:00
Alex Ant 743d5fc306 разделение формы для ввода персональных данных с регистратором; зародыш формы добавления пайщика в реестр. 2024-08-13 23:00:09 +05:00
Alex Ant 2f51516163 step to install | works on add user by api 2024-08-13 15:21:23 +05:00
Alex Ant b606b46aa4 страница настроек с кнопками выхода для пайщика 2024-08-05 14:31:27 +05:00
Alex Ant 5b65ff99a0 chore(release): publish
- coopback@1.7.29
 - coopdoc-generator-ts@1.0.73
 - cooptypes@1.0.18
 - coop-notificator@0.0.22
 - terminal@0.1.33
2024-08-05 12:36:01 +05:00
Alex Ant 74a3f52324 update 2024-08-05 12:32:23 +05:00
Alex Ant 7c2a4fdfa8 удаление потерявшейся папки 2024-08-05 12:05:05 +05:00
Alex Ant fecee3a098 fix naming 2024-08-05 11:56:29 +05:00
Alex Ant 45f7111fb9 chore(release): publish
- coopback@1.7.29-alpha.0
 - coopdoc-generator-ts@1.0.73-alpha.0
 - cooptypes@1.0.18-alpha.0
 - coop-notificator@0.0.22-alpha.0
 - terminal@0.1.33-alpha.0
2024-08-05 11:51:37 +05:00
Alex Ant f2e23a8062 перевыпуск ключа доступа 2024-08-05 11:38:10 +05:00
Alex Ant 36a3631f5e login with signature 2024-08-02 17:11:01 +05:00
Alex Ant a758d19a9f обновление mongoose, интеграция eslint и активация typescript проверок 2024-08-01 14:06:01 +05:00
Alex Ant aac3e53a30 eslint repair 2024-08-01 13:06:28 +05:00
Alex Ant 85d87ee210 FIX !!! 2024-07-31 20:48:38 +05:00
Alex Ant 2f3ee80ed4 Merge branch 'main' into dev 2024-07-31 17:26:20 +05:00
Alex Ant 3632bea54e chore(release): publish
- terminal@0.1.32
2024-07-31 17:26:08 +05:00
Alex Ant 4a0521751a update 2024-07-31 17:24:44 +05:00
Alex Ant 7d6266c241 chore(release): publish
- terminal@0.1.32-alpha.0
2024-07-31 16:28:53 +05:00
Alex Ant 1d69b9ffea Рабочие столы v1 2024-07-31 16:24:50 +05:00
Alex Ant 72674cba10 chore(release): publish
- coopback@1.7.28
 - terminal@0.1.31
2024-07-26 18:42:47 +05:00
Alex Ant 495a8e2d1e chore(release): publish
- coopback@1.7.28-alpha.0
 - terminal@0.1.31-alpha.0
2024-07-26 18:41:33 +05:00
Alex Ant 95ae221d60 Добавление платежных реквизитов | удаление | просмотр | свайп в рамках страницы 2024-07-26 18:38:14 +05:00
Alex Ant 857f746da1 bug fix 2024-07-26 11:03:52 +05:00
Alex Ant 0e2e3052b1 works 2024-07-25 21:00:04 +05:00
Alex Ant 167c04cc3f commit 2024-07-25 20:47:42 +05:00
Alex Ant 043f51479a looking for bug #2 2024-07-25 20:30:56 +05:00
Alex Ant 921282df81 looking for bug 2024-07-25 20:20:44 +05:00
Alex Ant b3c641501c migration 2024-07-25 20:07:44 +05:00
746 changed files with 29908 additions and 23978 deletions
Vendored
BIN
View File
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
node_modules
dist
+14
View File
@@ -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,
}
+124
View File
@@ -0,0 +1,124 @@
# 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.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))
+39
View File
@@ -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
View File
@@ -0,0 +1,10 @@
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install -g pnpm lerna
RUN pnpm install
RUN lerna run build
+15 -1
View File
@@ -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
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
+8
View File
@@ -0,0 +1,8 @@
.idea/
.vscode/
node_modules/
build/
tmp/
temp/
dist/
.env
+4
View File
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}
+16
View File
@@ -0,0 +1,16 @@
# 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.6](https://github.com/coopenomics/monocoop/compare/v2.1.5...v2.1.6) (2024-10-30)
**Note:** Version bump only for package @coopenomics/controller-next
## [0.0.2](https://github.com/coopenomics/provider/compare/v0.0.2-alpha.0...v0.0.2) (2024-10-23)
**Note:** Version bump only for package backend
+11
View File
@@ -0,0 +1,11 @@
FROM node:18-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable pnpm \
&& corepack prepare pnpm@latest --activate
WORKDIR /app
COPY package.json ./
+99
View File
@@ -0,0 +1,99 @@
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
</p>
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Project setup
```bash
$ pnpm install
```
## Compile and run the project
```bash
# development
$ pnpm run start
# watch mode
$ pnpm run start:dev
# production mode
$ pnpm run start:prod
```
## Run tests
```bash
# unit tests
$ pnpm run test
# e2e tests
$ pnpm run test:e2e
# test coverage
$ pnpm run test:cov
```
## Deployment
When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information.
If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps:
```bash
$ pnpm install -g mau
$ mau deploy
```
With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure.
## Resources
Check out a few resources that may come in handy when working with NestJS:
- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework.
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks.
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).
+8
View File
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
+7
View File
@@ -0,0 +1,7 @@
{
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "7.9.0"
}
}
+103
View File
@@ -0,0 +1,103 @@
{
"name": "@coopenomics/controller-next",
"version": "2.1.6",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "node dist/main.js",
"generate-api-client": "openapi --input swagger.json --output ./client/generated",
"dev": "nest start --watch",
"dev:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"typeorm": "typeorm-ts-node-commonjs",
"migration:generate": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:generate -d ./data-source.ts",
"migration:run": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:run -d ./data-source.ts"
},
"dependencies": {
"@litehex/node-vault": "^1.0.0",
"@nestjs/bull": "^10.2.1",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.2.3",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/schedule": "^4.1.1",
"@nestjs/swagger": "^7.4.2",
"@nestjs/typeorm": "^10.0.2",
"@wharfkit/antelope": "^1.0.12",
"@wharfkit/contract": "^1.1.5",
"@wharfkit/session": "^1.4.0",
"@wharfkit/wallet-plugin-privatekey": "^1.1.0",
"axios": "^1.2.1",
"bull": "^4.16.3",
"cooptypes": "^2.1.2",
"docxtemplater": "^3.51.1",
"dotenv": "^16.4.5",
"faker": "^5.1.0",
"fs-extra": "^11.2.0",
"glob": "^11.0.0",
"joi": "^17.13.3",
"jsonwebtoken": "^8.5.1",
"libreoffice-convert": "^1.6.0",
"nextcloud-link": "^1.2.9",
"pg": "^8.4.0",
"pizzip": "^3.1.7",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
"swagger-ui-express": "^5.0.1",
"typeorm": "0.3.20",
"zod": "^3.23.8"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/bull": "^4.10.0",
"@types/express": "^5.0.0",
"@types/fs-extra": "^11.0.4",
"@types/jest": "^29.5.2",
"@types/node": "^16.11.10",
"@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"eslint": "^9.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"openapi-typescript-codegen": "^0.29.0",
"prettier": "^3.0.0",
"source-map-support": "^0.5.21",
"supertest": "^7.0.0",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.3",
"ts-node": "10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "5.6.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
+55
View File
@@ -0,0 +1,55 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import * as glob from 'glob';
import * as path from 'path';
import { validateEnv } from './env.validator';
import { BullModule as NestBullModule } from '@nestjs/bull';
// Функция для динамического импорта модулей
function dynamicImportModules(): any[] {
const modules = [] as any[];
const modulePaths = glob.sync(
path.join(__dirname, 'modules/*/*.module.{ts,js}'),
);
modulePaths.forEach((modulePath) => {
const module = require(modulePath);
const moduleName = Object.keys(module)[0]; // Получаем имя экспортируемого модуля
modules.push(module[moduleName]);
});
return modules;
}
@Module({
imports: [
...dynamicImportModules(), // Динамически импортируем все найденные модули
ConfigModule.forRoot({
isGlobal: true, // Чтобы .env был доступен глобально
validate: validateEnv, // Заменяем стандартную валидацию на свою
}),
NestBullModule.forRoot({
redis: {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT as string),
password: process.env.REDIS_PASSWORD,
},
}),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.PROVIDER_DB_HOST,
port: Number(process.env.PROVIDER_DB_PORT),
username: process.env.PROVIDER_DB_USERNAME,
password: process.env.PROVIDER_DB_PASSWORD,
database: process.env.PROVIDER_DB_DATABASE,
entities: [path.join(__dirname, 'modules/*/entities/*.{ts,js}')],
migrations: ['migrations/*.ts'], // Указываем путь на исходные TS файлы миграций
synchronize: true, //поменять когда-нибудь потом
logging: false,
}),
],
controllers: [],
providers: [],
})
export class AppModule {}
@@ -0,0 +1,76 @@
import { z } from 'zod';
import { config } from 'dotenv';
// Загружаем переменные окружения
config();
// Определяем схему валидации без необходимости указывать сообщения
const envSchema = z.object({
NODE_ENV: z
.enum(['development', 'production', 'test'])
.default('development'),
PROVIDER_DB_HOST: z.string(),
PROVIDER_DB_PORT: z
.string()
.transform((val) => parseInt(val, 10))
.default('5432'),
PROVIDER_DB_USERNAME: z.string(),
PROVIDER_DB_PASSWORD: z.string(),
PROVIDER_DB_DATABASE: z.string(),
CHAIN_URL: z.string().url(),
CHAIN_ID: z.string(),
SEMAPHORE_API_URL: z.string().url(),
SEMAPHORE_USERNAME: z.string(),
SEMAPHORE_PASSWORD: z.string(),
SEMAPHORE_PROJECT_ID: z.string().transform((val) => parseInt(val, 10)),
SEMAPHORE_INVENTORY_ID: z.string().transform((val) => parseInt(val, 10)),
SEMAPHORE_SSH_KEY_ID: z.string().transform((val) => parseInt(val, 10)),
SEMAPHORE_INVENTORY_NAME: z.string(),
SEMAPHORE_TEMPLATE_ID: z.string().transform((val) => parseInt(val, 10)),
SERVER_IP: z.string().refine(
(ip) => {
const ipRegex =
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
return ipRegex.test(ip);
},
{ message: 'Invalid IP address' },
),
SEMAPHORE_SETUP_PLAYBOOK: z.string(),
PROVIDER_ACCOUNT: z.string(),
PROVIDER_WIF: z.string(),
START_TOKENS: z.string(), // если нужно можно добавить валидацию формата
SERVER_SECRET: z.string(),
PROVIDER_URL: z.string().url(),
// VAULT
VAULT_TOKEN: z.string(),
VAULT_ENDPOINT: z.string(),
// NEXTCLOUD
NEXTCLOUD_BASE_URL: z.string(),
NEXTCLOUD_USERNAME: z.string(),
NEXTCLOUD_PASSWORD: z.string(),
// REDIS
REDIS_HOST: z.string(),
REDIS_PORT: z.string(),
REDIS_PASSWORD: z.string(),
});
// Валидация переменных окружения с единым сообщением об отсутствии полей
export function validateEnv() {
const parsedEnv = envSchema.safeParse(process.env);
if (!parsedEnv.success) {
const formattedErrors = parsedEnv.error.format();
const errorMessages = Object.keys(formattedErrors)
.filter((key) => key !== '_errors')
.map((key) => `${key}: параметр не установлен`)
.join('\n');
console.error('❌ Ошибка конфигурации:\n', errorMessages);
process.exit(1); // Завершаем приложение в случае ошибки
}
return parsedEnv.data; // Возвращаем валидированные данные
}
+35
View File
@@ -0,0 +1,35 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Logger } from '@nestjs/common';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { writeFileSync } from 'fs';
import { join } from 'path';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Настройка Swagger
const config = new DocumentBuilder()
.setTitle('API Documentation')
.setDescription('API description')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
// Создание файла swagger.json
const swaggerFilePath = join(process.cwd(), 'swagger.json');
writeFileSync(swaggerFilePath, JSON.stringify(document, null, 2), {
encoding: 'utf8',
});
Logger.log(`Swagger JSON file generated at: ${swaggerFilePath}`, 'Swagger');
// Настройка Swagger UI
SwaggerModule.setup('api-docs', app, document);
await app.listen(process.env.PORT ?? 4090);
Logger.log(
`Application started on ${process.env.PORT ?? 4090} port`,
'System',
);
}
bootstrap();
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { BlockchainService } from './blockchain.service';
@Module({
imports: [],
controllers: [],
providers: [BlockchainService],
exports: [BlockchainService],
})
export class BlockchainModule {}
@@ -0,0 +1,147 @@
import { Action, APIClient, PrivateKey } from '@wharfkit/antelope';
import { ContractKit, Table } from '@wharfkit/contract';
import { Session, type TransactResult } from '@wharfkit/session';
import { WalletPluginPrivateKey } from '@wharfkit/wallet-plugin-privatekey';
import { Injectable, Logger } from '@nestjs/common';
import { SystemContract } from 'cooptypes';
@Injectable()
export class BlockchainService {
private readonly logger = new Logger(BlockchainService.name);
public apiClient: APIClient;
public contractKit: ContractKit;
public session: Session;
constructor() {
this.apiClient = new APIClient({
url: process.env.CHAIN_URL,
});
this.contractKit = new ContractKit({
client: this.apiClient,
});
this.session = new Session({
actor: process.env.PROVIDER_ACCOUNT,
permission: 'active',
chain: {
id: process.env.CHAIN_ID as string,
url: process.env.CHAIN_URL as string,
},
walletPlugin: new WalletPluginPrivateKey(
PrivateKey.fromString(process.env.PROVIDER_WIF as string),
),
});
}
public transact = async (
actionOrActions: any | any[],
broadcast = true,
): Promise<TransactResult | undefined> => {
if (Array.isArray(actionOrActions)) {
return await this.sendActions(actionOrActions, broadcast);
} else {
return await this.sendAction(actionOrActions, broadcast);
}
};
public formActionFromAbi = async (action: any) => {
const { abi } = (await this.apiClient.v1.chain.get_abi(action.account)) ?? {
abi: undefined,
};
return Action.from(action, abi);
};
public sendAction = async (action: any, broadcast: boolean) => {
const formedAction = await this.formActionFromAbi(action);
return this.session.transact(
{
action: formedAction,
},
{ broadcast },
);
};
public sendActions = async (actions: any[], broadcast: boolean) => {
const data: Action[] = [];
for (const action of actions) {
const formedAction = await this.formActionFromAbi(action);
data.push(formedAction);
}
return this.session.transact(
{
actions: data,
},
{ broadcast },
);
};
public async transfer(
from: string,
to: string,
quantity: string,
memo: string,
): Promise<void> {
const data = {
account: 'eosio.token',
name: 'transfer',
authorization: [
{
actor: process.env.PROVIDER_ACCOUNT as string,
permission: 'active',
},
],
data: {
from,
to,
quantity,
memo,
},
};
await this.transact(data);
}
public async powerup(
data: SystemContract.Actions.Powerup.IPowerup,
): Promise<void> {
const action = {
account: 'eosio',
name: SystemContract.Actions.Powerup.actionName,
authorization: [
{
actor: process.env.PROVIDER_ACCOUNT as string,
permission: 'active',
},
],
data,
};
await this.transact(action);
}
public async getAllTableRows(
code: string,
scope: string,
name: string,
): Promise<any[]> {
const { abi } = await this.apiClient.v1.chain.get_abi(code);
if (!abi) throw new Error(`ABI контракта ${code} не найден`);
const table = new Table({
abi,
account: code,
name: name,
client: this.apiClient,
});
const rows = await table.all({ scope });
return JSON.parse(JSON.stringify(rows));
}
}
@@ -0,0 +1,29 @@
import {
ApiBody,
ApiOperation,
ApiParam,
ApiResponse,
ApiTags,
getSchemaPath,
} from '@nestjs/swagger';
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { BullService } from './bull.service';
import { JobEntity } from './entities/job.entity';
@ApiTags('jobs') // Добавляет тег для группировки в Swagger UI
@Controller('jobs') // Базовый маршрут для всех методов этого контроллера
@Controller()
export class BullController {
constructor(private readonly bullService: BullService) {}
@ApiOperation({ summary: 'Получить все задачи' })
@ApiResponse({
status: 200,
description: 'Возвращает массив задач',
type: [JobEntity],
}) // Указываем, что возвращается массив
@Get('/all')
async getAllJobs(): Promise<JobEntity[]> {
return this.bullService.getAllJobs();
}
}
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { BullService } from './bull.service';
import { JobEntity } from './entities/job.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BullController } from './bull.controller';
@Module({
imports: [TypeOrmModule.forFeature([JobEntity])],
controllers: [BullController],
providers: [BullService],
exports: [BullService],
})
export class BullModule {}
@@ -0,0 +1,168 @@
// modules/bull/bull.service.ts
import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
import { Queue, Job, JobId, JobOptions } from 'bull';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { JobEntity } from './entities/job.entity';
// Интерфейс для возврата данных после выполнения задачи
export interface JobResult<T> {
result: T;
}
@Injectable()
export class BullService implements OnModuleInit {
private readonly logger = new Logger(BullService.name);
private queues: Map<string, Queue> = new Map();
constructor(
@InjectRepository(JobEntity)
private readonly jobRepository: Repository<JobEntity>,
) {}
onModuleInit() {
this.logger.log('BullService initialized');
}
// Регистрация очереди по её имени, если она уже не была зарегистрирована
registerQueue(queueName: string, queue: Queue) {
if (!this.queues.has(queueName)) {
this.queues.set(queueName, queue);
this.logger.log(`Очередь зарегистрирована: ${queueName}`);
} else {
this.logger.warn(`Очередь ${queueName} уже зарегистрирована.`);
}
}
// Получение очереди по имени
public getQueue(queueName: string): Queue {
const queue = this.queues.get(queueName);
if (!queue) {
throw new Error(`Очередь не зарегистрирована: ${queueName}`);
}
return queue;
}
// Метод для добавления задачи в очередь с дженериком T для результата
async addJob<T>(
queueName: string,
jobType: string,
jobData: any,
options: JobOptions = {
attempts: 3, // 3 попытки в случае сбоя
backoff: 1000, // задержка перед повтором в 1000 мс
},
): Promise<Job<JobResult<T>>> {
this.logger.log(
`Добавление задачи типа "${jobType}" в очередь "${queueName}" с данными: ${JSON.stringify(
jobData,
)}`,
);
const queue = this.getQueue(queueName);
// Создаем задачу с типом результата T
const job = await queue.add(jobType, jobData, options);
// Сохраняем задачу в базе данных
await this.saveJobToDatabase(job, queueName);
return job as Job<JobResult<T>>;
}
// Метод для обновления прогресса задачи
async updateJobProgress(job: Job, progress: number): Promise<void> {
await job.progress(progress);
this.logger.log(`Обновлен прогресс задачи ${job.id} до ${progress}%`);
// Обновляем запись в базе данных
await this.saveJobToDatabase(job, job.queue.name);
}
// Метод для сохранения результата задачи с дженериком T
async saveJobResult<T>(job: Job<JobResult<T>>, result: T): Promise<void> {
job.data.result = result; // Сохраняем результат в данных задачи
await job.update(job.data); // Обновляем задачу с результатом
this.logger.log(
`Результат задачи ${job.id} сохранен: ${JSON.stringify(result)}`,
);
// Обновляем запись в базе данных
await this.saveJobToDatabase(job, job.queue.name);
}
// Метод для пометки задачи как завершенной
async completeJob(job: Job): Promise<void> {
this.logger.log(`Задача ${job.id} успешно завершена.`);
await job.moveToCompleted('completed', true);
// Обновляем запись в базе данных
await this.saveJobToDatabase(job, job.queue.name);
}
// Метод для пометки задачи как провалившейся
async failJob(job: Job, error: any): Promise<void> {
this.logger.error(
`Задача ${job.id} завершилась с ошибкой: ${error.message}`,
);
await job.moveToFailed({ message: error.message }, true);
// Обновляем запись в базе данных
await this.saveJobToDatabase(job, job.queue.name);
}
// Метод для сохранения или обновления задачи в базе данных
private async saveJobToDatabase(job: Job, queueName: string) {
const existingJob = await this.jobRepository.findOne({
where: { jobId: job.id.toString() },
});
const progress: number = (
typeof job.progress === 'number' ? job.progress : 0
) as number;
const jobData = {
jobId: job.id.toString(),
queueName,
data: JSON.stringify(job.data),
status: await job.getState(),
progress,
updatedAt: new Date(),
};
if (existingJob) {
// Обновляем существующую запись
await this.jobRepository.update(existingJob.id, jobData);
} else {
// Создаем новую запись
const jobEntity = this.jobRepository.create({
...jobData,
});
await this.jobRepository.save(jobEntity);
}
}
// Получение задачи по ID
async getJob<T>(
queueName: string,
jobId: JobId,
): Promise<Job<JobResult<T>> | null> {
const queue = this.getQueue(queueName);
const job = await queue.getJob(jobId);
return job as Job<JobResult<T>>;
}
// Методы для извлечения задач из базы данных
async getAllJobs(): Promise<JobEntity[]> {
return await this.jobRepository.find();
}
async getJobsByQueue(queueName: string): Promise<JobEntity[]> {
return await this.jobRepository.find({ where: { queueName } });
}
async getJobByJobId(jobId: string): Promise<JobEntity | null> {
return await this.jobRepository.findOne({ where: { jobId } });
}
}
@@ -0,0 +1,36 @@
// entities/job.entity.ts
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity('jobs')
export class JobEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
jobId: string;
@Column()
queueName: string;
@Column('text')
data: string;
@Column()
status: string;
@Column({ type: 'float', default: 0 })
progress: number;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}
@@ -0,0 +1,8 @@
// src/cms/cms.module.ts
import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common';
import { CmsController } from './controllers/cms.controller';
@Module({
controllers: [CmsController],
})
export class CmsModule {}
@@ -0,0 +1,19 @@
// src/cms/cms.controller.ts
import { Controller, Get, Res, Param } from '@nestjs/common';
import { Response } from 'express';
import { join } from 'path';
@Controller('cms')
export class CmsController {
@Get('*') // Обрабатываем все запросы
serveCms(@Param() params: any, @Res() res: Response) {
const filePath = join(
__dirname,
'../../public/cms',
params[0] || 'index.html',
);
res.sendFile(filePath); // Возвращаем запрашиваемый файл
}
}
// <iframe src="https://yourdomain.com/cms?token=YOUR_JWT_TOKEN&product_id=123"></iframe>
@@ -0,0 +1,10 @@
// src/modules/nextcloud/nextcloud.module.ts
import { Module } from '@nestjs/common';
import { NextcloudService } from './nextcloud.service';
@Module({
providers: [NextcloudService],
exports: [NextcloudService],
})
export class NextcloudModule {}
@@ -0,0 +1,221 @@
import { Injectable, Logger } from '@nestjs/common';
import { NextcloudClient } from 'nextcloud-link';
import * as fs from 'fs';
import * as path from 'path';
interface OcsNewUser {
userid: string;
password?: string;
email?: string;
displayName?: string;
groups?: string[];
subadmin?: string[];
quota?: number;
language?: string;
}
enum OcsShareType {
user = 0,
group = 1,
publicLink = 3,
federatedCloudShare = 6,
}
enum OcsSharePermissions {
default = -1,
read = 1,
update = 2,
create = 4,
delete = 8,
share = 16,
all = 31,
}
@Injectable()
export class NextcloudService {
private readonly logger = new Logger(NextcloudService.name);
private client: NextcloudClient;
constructor() {
// Инициализация клиента Nextcloud
this.client = new NextcloudClient({
url: process.env.NEXTCLOUD_BASE_URL as string, // URL вашего Nextcloud, например 'https://your-nextcloud-instance.com'
username: process.env.NEXTCLOUD_USERNAME, // Имя пользователя (администратор)
password: process.env.NEXTCLOUD_PASSWORD, // Пароль или пароль приложения
});
}
// Загрузка файла в Nextcloud
async uploadFile(
localFilePath: string,
remoteFilePath: string,
): Promise<void> {
try {
// Чтение файла как Buffer
const fileBuffer = fs.readFileSync(localFilePath);
await this.client.put(remoteFilePath, fileBuffer);
this.logger.log(`Файл загружен в Nextcloud: ${remoteFilePath}`);
} catch (error: any) {
this.logger.error(`Ошибка при загрузке файла: ${error.message}`);
throw new Error(`Не удалось загрузить файл: ${error.message}`);
}
}
async touchFolder(path: string): Promise<void> {
try {
const share = await this.client.touchFolder(path);
} catch (error: any) {
this.logger.error(`Ошибка при создании папки: ${error.message}`);
throw new Error(`Не удалось создать папку: ${error.message}`);
}
}
// Получение публичной ссылки на файл
async shareFile(remoteFilePath: string): Promise<string> {
try {
const share = await this.client.shares.add(
remoteFilePath,
OcsShareType.publicLink, // 3 соответствует публичной ссылке
undefined,
OcsSharePermissions.read, // 1 означает только чтение
);
const shareUrl = share.url;
if (!shareUrl) {
throw new Error(`Не удалось получить ссылку на файл ${remoteFilePath}`);
} else {
this.logger.log(`Файл расшарен по ссылке: ${shareUrl}`);
return shareUrl;
}
} catch (error: any) {
this.logger.error(`Ошибка при шаринге файла: ${error.message}`);
throw new Error(`Не удалось расшарить файл: ${error.message}`);
}
}
// Создание нового пользователя
async createUser(newUser: OcsNewUser): Promise<void> {
try {
await this.client.users.add(newUser); // Передаем объект newUser
this.logger.log(`Пользователь ${newUser.userid} успешно создан`);
} catch (error: any) {
this.logger.error(`Ошибка при создании пользователя: ${error.message}`);
throw new Error(`Не удалось создать пользователя: ${error.message}`);
}
}
// Добавление пользователя в группу
async addUserToGroup(username: string, groupName: string): Promise<void> {
try {
await this.client.users.addToGroup(username, groupName);
this.logger.log(
`Пользователь ${username} добавлен в группу ${groupName}`,
);
} catch (error: any) {
this.logger.error(
`Ошибка при добавлении пользователя в группу: ${error.message}`,
);
throw new Error(
`Не удалось добавить пользователя в группу: ${error.message}`,
);
}
}
// Создание новой группы
async createGroup(groupName: string): Promise<void> {
try {
await this.client.groups.add(groupName);
this.logger.log(`Группа ${groupName} успешно создана`);
} catch (error: any) {
this.logger.error(`Ошибка при создании группы: ${error.message}`);
throw new Error(`Не удалось создать группу: ${error.message}`);
}
}
// Назначение пользователя администратором группы
async promoteUserToGroupAdmin(
username: string,
groupName: string,
): Promise<void> {
try {
await this.client.users.addSubAdminToGroup(username, groupName);
this.logger.log(
`Пользователь ${username} назначен администратором группы ${groupName}`,
);
} catch (error: any) {
this.logger.error(
`Ошибка при назначении администратора группы: ${error.message}`,
);
throw new Error(
`Не удалось назначить администратора группы: ${error.message}`,
);
}
}
// Метод для создания групповой папки с разрешениями только на чтение для всех
async createReadOnlyGroupFolder(
mountpoint: string,
groupId: string,
): Promise<void> {
try {
// Создаем групповую папку и получаем её ID
const folderId = await this.client.groupfolders.addFolder(mountpoint);
this.logger.log(`Групповая папка ${mountpoint} создана с ID ${folderId}`);
// Добавляем группу в папку
await this.client.groupfolders.addGroup(folderId, groupId);
this.logger.log(
`Группа ${groupId} добавлена в групповую папку ${folderId}`,
);
// Устанавливаем разрешения только на чтение для группы
await this.client.groupfolders.setPermissions(
folderId,
groupId,
OcsSharePermissions.read,
);
this.logger.log(
`Установлены разрешения только на чтение для группы ${groupId} в папке ${folderId}`,
);
} catch (error: any) {
this.logger.error(
`Ошибка при создании групповой папки: ${error.message}`,
);
throw new Error(`Не удалось создать групповую папку: ${error.message}`);
}
}
// Метод для создания групповой папки с полными разрешениями для всех участников
async createFullAccessGroupFolder(
mountpoint: string,
groupId: string,
): Promise<void> {
try {
// Создаем групповую папку и получаем её ID
const folderId = await this.client.groupfolders.addFolder(mountpoint);
this.logger.log(`Групповая папка ${mountpoint} создана с ID ${folderId}`);
// Добавляем группу в папку
await this.client.groupfolders.addGroup(folderId, groupId);
this.logger.log(
`Группа ${groupId} добавлена в групповую папку ${folderId}`,
);
// Устанавливаем полные разрешения для группы
await this.client.groupfolders.setPermissions(
folderId,
groupId,
OcsSharePermissions.all,
);
this.logger.log(
`Установлены полные разрешения для группы ${groupId} в папке ${folderId}`,
);
} catch (error: any) {
this.logger.error(
`Ошибка при создании групповой папки: ${error.message}`,
);
throw new Error(`Не удалось создать групповую папку: ${error.message}`);
}
}
}
@@ -0,0 +1,29 @@
// src/modules/notificator/notificator.module.ts
import { Module } from '@nestjs/common';
import { BullModule as NestBullModule } from '@nestjs/bull';
import { NotificatorService } from './notificator.service';
import { BullModule } from '../bull/bull.module';
import { BullService } from '../bull/bull.service';
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';
@Module({
imports: [
// Регистрируем очередь 'notificationQueue' на уровне модуля
NestBullModule.registerQueue({
name: 'notificationQueue',
}),
BullModule,
],
providers: [NotificatorService],
})
export class NotificatorModule {
constructor(
private readonly bullService: BullService,
@InjectQueue('notificationQueue') private readonly notificationQueue: Queue,
) {
// Регистрируем очередь в BullService с именем и объектом очереди
this.bullService.registerQueue('notificationQueue', this.notificationQueue);
}
}
@@ -0,0 +1,33 @@
// modules/notificator/notificator.service.ts
import { Injectable } from '@nestjs/common';
import { BullService } from '../bull/bull.service';
import { JobOptions } from 'bull';
@Injectable()
export class NotificatorService {
constructor(private readonly bullService: BullService) {}
async notifyUser(
userId: string,
sequential: boolean = false, // Для последовательного или параллельного выполнения
): Promise<string> {
const queueName = 'notificationQueue';
const jobData = {
taskName: 'User Notification',
userId,
sequential,
};
// Добавляем задачу в очередь
const job = await this.bullService.addJob(
queueName,
'email', // Тип задачи, которую будет обрабатывать процессор
jobData,
{},
);
// Возвращаем ID задачи для отслеживания прогресса
return job.id as string;
}
}
@@ -0,0 +1,41 @@
// modules/notificator/processors/email.processor.ts
import { Processor, Process } from '@nestjs/bull';
import { Job } from 'bull';
import { Logger } from '@nestjs/common';
import { BullService } from '../../bull/bull.service';
@Processor('notificationQueue')
export class EmailProcessor {
private readonly logger = new Logger(EmailProcessor.name);
constructor(private readonly bullService: BullService) {}
@Process('email')
async handleEmailTask(job: Job<any>): Promise<void> {
this.logger.log(`Processing email task with id ${job.id}`);
try {
// Реализация отправки email
await this.sendEmail(job.data);
// Обновление прогресса задачи до 100%
await this.bullService.updateJobProgress(job, 100);
// Пометка задачи как завершенной
await this.bullService.completeJob(job);
} catch (error: any) {
this.logger.error(`Error in email task ${job.id}: ${error.message}`);
// Пометка задачи как провалившейся
await this.bullService.failJob(job, error);
}
}
private async sendEmail(data: any): Promise<void> {
// Логика отправки email
await new Promise((resolve) => setTimeout(resolve, 500)); // Заглушка для симуляции отправки
// Если нужно симулировать ошибку, можно выбросить исключение:
// throw new Error('Email sending failed');
}
}
+57
View File
@@ -0,0 +1,57 @@
{
"openapi": "3.0.0",
"paths": {
"/jobs/all": {
"get": {
"operationId": "BullController_getAllJobs",
"summary": "Получить все задачи",
"parameters": [],
"responses": {
"200": {
"description": "Возвращает массив задач",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/JobEntity"
}
}
}
}
}
},
"tags": [
"jobs"
]
}
},
"/cms/*": {
"get": {
"operationId": "CmsController_serveCms",
"parameters": [],
"responses": {
"200": {
"description": ""
}
}
}
}
},
"info": {
"title": "API Documentation",
"description": "API description",
"version": "1.0",
"contact": {}
},
"tags": [],
"servers": [],
"components": {
"schemas": {
"JobEntity": {
"type": "object",
"properties": {}
}
}
}
}
Binary file not shown.
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": "../src/",
"testEnvironment": "node",
"testRegex": ".test.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2020", // Более новый target для Node.js
"lib": ["es2020"],
"esModuleInterop": true,
"moduleResolution": "node",
"sourceMap": true,
"outDir": "./dist",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"strict": true,
"strictPropertyInitialization": false // Опция для инъекции зависимостей
},
"exclude": ["dist", "build"]
}
+68
View File
@@ -3,6 +3,74 @@
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
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "pkg-placeholder",
"version": "1.0.17",
"version": "2.1.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "pkg-placeholder",
"version": "1.0.17",
"version": "2.1.3",
"license": "MIT",
"devDependencies": {
"@antfu/eslint-config": "^2.16.0",
+4 -3
View File
@@ -1,8 +1,8 @@
{
"name": "cooparser-ts",
"type": "module",
"version": "1.0.17",
"private": true,
"version": "2.1.3",
"private": false,
"packageManager": "pnpm@9.0.6",
"description": "",
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
@@ -82,5 +82,6 @@
"unbuild": "^2.0.0",
"vite": "^5.2.10",
"vitest": "^1.5.2"
}
},
"gitHead": "7b59ea4b96991ed06abdc92e6d7835632845e3a5"
}
+19 -225
View File
@@ -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)
})
}
+7 -10
View File
@@ -11,7 +11,6 @@ export class Database {
private sync: Collection | undefined
constructor() {
console.log('mongo2: ', mongoUri)
this.client = new MongoClient(mongoUri)
}
@@ -52,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[],
@@ -80,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()
+1 -1
View File
@@ -11,7 +11,6 @@ function getEnvVar(key: string): string {
return envVar
}
export const node_env = getEnvVar('NODE_ENV')
console.log('ENV: ', node_env)
export const eosioApi = getEnvVar('API')
export const shipApi = getEnvVar('SHIP')
export const mongoUri = `${getEnvVar('MONGO_EXPLORER_URI')}${node_env === 'test' ? '-test' : ''}`
@@ -55,6 +54,7 @@ export const subsribedActions: IActionConfig[] = [
{ code: 'soviet', action: 'joincoopdec' },
{ code: 'soviet', action: 'updateboard', notify: true },
{ code: 'soviet', action: 'createboard', notify: true },
]
// --------------------------
BIN
View File
Binary file not shown.
-31
View File
@@ -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"
}
]
}
}
+25
View File
@@ -0,0 +1,25 @@
{
"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": "off",
"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"
}
}
+4 -1
View File
@@ -12,4 +12,7 @@ yarn-error.log
coverage
.env-mixed
.env-local
.env-local
logs
logs/*
+4 -2
View File
@@ -1,6 +1,8 @@
{
"CodeGPT.apiKey": "OpenAI",
"cSpell.words": [
"subcode"
]
],
"editor.formatOnSave": true,
"typescript.tsdk": "node_modules/typescript/lib",
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
+156
View File
@@ -3,6 +3,162 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
## [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
+25 -33
View File
@@ -1,47 +1,39 @@
# Базовый образ Node.js на Alpine
FROM node:alpine AS base
# ENV PNPM_HOME="/pnpm"
# ENV PATH="$PNPM_HOME:$PATH"
# Включение pnpm
RUN corepack enable pnpm \
&& corepack prepare pnpm@latest --activate
# Устанавливаем рабочую директорию
WORKDIR /app
# Копируем package.json
COPY package.json ./
# Install dependencies for Puppeteer
# Установка системных зависимостей для WeasyPrint и создание виртуального окружения
RUN apk add --no-cache \
chromium \
nss \
freetype \
freetype-dev \
harfbuzz \
ca-certificates \
ttf-freefont \
&& apk add --no-cache --virtual .build-deps \
curl \
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/*
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
# Установка пути к виртуальному окружению в PATH
ENV PATH="/venv/bin:$PATH"
# RUN mkdir -p /usr/src/node-app && chown -R node:node /usr/src/node-app
# Проверка наличия команды weasyprint
RUN weasyprint --version
# WORKDIR /usr/src/node-app
# COPY package.json pnpm-lock.yaml ./
# USER node
# FROM base AS prod-deps
# RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
# FROM base AS build
# RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
# COPY --chown=node:node . .
# EXPOSE 3000
# CMD ["npx", "ts-node", "src/index.ts"]
# CMD для запуска weasyprint
# CMD ["weasyprint", "input.html", "output.pdf"]
+2 -1
View File
@@ -1,4 +1,4 @@
# COOPERATIVE CONTROLLER
# COOPERATIVE CONTROLLER.
[![Build Status](https://travis-ci.org/hagopj13/node-express-boilerplate.svg?branch=master)](https://travis-ci.org/hagopj13/node-express-boilerplate)
[![Coverage Status](https://coveralls.io/repos/github/hagopj13/node-express-boilerplate/badge.svg?branch=master)](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:
+4 -4
View File
@@ -1,12 +1,12 @@
{
"name": "create-nodejs-express-app",
"version": "1.7.27",
"version": "2.1.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "create-nodejs-express-app",
"version": "1.7.27",
"version": "2.1.6",
"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,7 +54,7 @@
"@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",
+21 -5
View File
@@ -1,16 +1,20 @@
{
"name": "coopback",
"version": "1.7.27",
"name": "@coopenomics/controller",
"version": "2.1.6",
"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",
@@ -79,17 +83,23 @@
"faker": "^5.1.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": "^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",
@@ -99,21 +109,27 @@
"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",
@@ -1,4 +1,5 @@
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';
@@ -7,7 +8,7 @@ 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);
+1 -1
View File
@@ -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) {
+1 -7
View File
@@ -15,17 +15,12 @@ async function init() {
await mongoose.connect(config.mongoose.url, config.mongoose.options);
const wif = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3';
const password = await crypto.createHash('sha256').update(wif).digest('hex');
const hashed_password = await hash(password, 8);
console.log('password: ', hashed_password);
await userService.createUser({
username: 'ant',
email: 'dacom.dark.sun@gmail.com',
password,
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
role: 'chairman',
role: 'user',
type: 'individual',
individual_data: {
first_name: 'Иван',
@@ -46,7 +41,6 @@ async function init() {
const voskhod = await userService.createUser({
username: 'voskhod',
email: 'chairman.voskhod@gmail.com',
password,
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
role: 'user',
type: 'organization',
@@ -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();
+1 -1
View File
@@ -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,
});
Binary file not shown.
-8
View File
@@ -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') {
+16 -8
View File
@@ -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,17 @@ 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('имя аккаунта кооператива'),
GRAPHQL_SERVICE: Joi.string().description('адрес сервиса GRAPHQL').default('http://localhost:4090'),
})
.unknown();
@@ -39,13 +42,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 +59,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 +72,6 @@ export default {
},
from: envVars.EMAIL_FROM,
},
coopname: envVars.COOPNAME,
graphql_service: envVars.GRAPHQL_SERVICE,
};
+25 -4
View File
@@ -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',
}),
],
});
+4 -4
View File
@@ -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,
}
};
+3 -4
View File
@@ -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);
}
+30 -4
View File
@@ -1,8 +1,34 @@
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: [''],
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);
+1
View File
@@ -3,4 +3,5 @@ export const tokenTypes = {
REFRESH: 'refresh',
RESET_PASSWORD: 'resetPassword',
VERIFY_EMAIL: 'verifyEmail',
INVITE: 'invite',
};
@@ -1,17 +1,15 @@
import httpStatus from 'http-status';
import catchAsync from '../utils/catchAsync';
import { authService, userService, tokenService, emailService, blockchainService } from '../services';
import { RForgotPassword, RRefreshTokens, RResetPassword, RSendNotification, RVerifyEmail } from '../types';
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 { email, password } = req.body;
const user = await authService.loginUserWithEmailAndPassword(email, 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 });
@@ -27,14 +25,14 @@ export const refreshTokens = catchAsync(async (req: RRefreshTokens, res: any) =>
res.send(tokens);
});
export const forgotPassword = catchAsync(async (req: RForgotPassword, 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: RResetPassword, 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();
});
@@ -1,26 +1,24 @@
import catchAsync from '../utils/catchAsync';
import { coopService, documentService } from '../services';
import { Cooperative } from 'cooptypes';
import { generator } from '../services/document.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 documentService.buildComplexDocument(action);
@@ -30,7 +28,7 @@ export const loadAgenda = catchAsync(async (req, res) => {
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,18 +1,8 @@
import catchAsync from '../utils/catchAsync';
import { documentService } from '../services/index';
import type {
IGetDocuments,
RDeletePaymentMethod,
RGenerate,
RGetDocuments,
RGetListPaymentMethods,
RSavePaymentMethod,
} from '../types';
import type { RGenerate, RGetDocuments } from '../types';
import httpStatus from 'http-status';
import pick from '../utils/pick';
import { IGetResponse } from '../types/common';
import { Cooperative } from 'cooptypes';
import ApiError from '../utils/ApiError';
export const generateDocument = catchAsync(async (req: RGenerate, res) => {
const document = await documentService.generateDocument(req.body);
@@ -21,11 +11,11 @@ export const generateDocument = catchAsync(async (req: RGenerate, res) => {
});
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 = await documentService.queryDocuments(filter, page, limit);
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);
});
+6 -1
View File
@@ -1,6 +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 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();
});
@@ -0,0 +1,61 @@
import http from 'http-status';
import catchAsync from '../utils/catchAsync';
import { orderService } from '../services';
import { ICreateDeposit, ICreateInitialPayment } from '../types';
import { Request, Response } from 'express';
import httpStatus from 'http-status';
import pick from '../utils/pick';
import logger from '../config/logger';
import { getProvider } from '../services/payment.service';
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 provider = 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,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();
});
@@ -1,104 +0,0 @@
import http from 'http-status';
import catchAsync from '../utils/catchAsync';
import { paymentService } from '../services';
import {
ICreateDeposit,
ICreateInitialPayment,
RDeletePaymentMethod,
RGetListPaymentMethods,
RSavePaymentMethod,
} from '../types';
import { Request, Response } from 'express';
import * as ip from 'ip';
import { IGetResponse } from '../types/common';
import ApiError from '../utils/ApiError';
import httpStatus from 'http-status';
import { Cooperative } from 'cooptypes';
import { getUserByUsername } from '../services/user.service';
import { User } from '../models';
// Список разрешенных IP-адресов
// const allowedIPs = [
// '185.71.76.0/27',
// '185.71.77.0/27',
// '77.75.153.0/25',
// '77.75.156.11',
// '77.75.156.35',
// '77.75.154.128/25',
// '2a02:5180::/32'
// ];
// if (process.env.NODE_ENV === 'development')
// allowedIPs.push('127.0.0.1')
export const catchIPN = catchAsync(async (req: Request, res: Response) => {
// const ipAddr = ((req.headers['x-real-ip'] as string) || req.ip || '').replace(/^::ffff:/, '');
// console.log("ip: ", ipAddr);
// const isAllowed = allowedIPs.some(allowedIP => {
// if (allowedIP.includes('/')) { // Если это диапазон
// return ip.cidrSubnet(allowedIP).contains(ipAddr);
// }
// return allowedIP === ipAddr; // Если это конкретный IP
// });
// if (!isAllowed) {
// console.log("попытка доступа с запрещенного IP: ", ipAddr)
// return res.status(http.FORBIDDEN).send('Доступ запрещен');
// }
// console.log("on catch ipn", req.body);
res.status(http.OK).send();
paymentService.catchIPN(req.body);
});
export const createDeposit = catchAsync(async (req: any, res: Response) => {
const username = req.user.username;
const result = await paymentService.createDeposit(username, req.body as ICreateDeposit);
res.status(http.CREATED).send(result);
});
export const createInitialPayment = catchAsync(async (req: any, res: Response) => {
const username = req.user.username;
const result = await paymentService.createInitialOrder(username, req.body as ICreateInitialPayment);
res.status(http.CREATED).send(result);
});
export const listPaymentMethods = catchAsync(async (req: RGetListPaymentMethods, res) => {
const filter = req.params?.username ? { username: req.params.username } : {};
const methods: IGetResponse<any> = await paymentService.listPaymentMethods({ ...filter });
res.status(httpStatus.OK).send(methods);
});
export const addPaymentMethod = catchAsync(async (req: RSavePaymentMethod, res) => {
if (req.params?.username != req.body.username)
throw new ApiError(httpStatus.BAD_REQUEST, 'username в params и body должны совпадать');
const user = await getUserByUsername(req.body.username);
const paymentData: Cooperative.Payments.IPaymentData = {
username: req.body.username,
method_id: req.body.method_id,
user_type: user.type,
method_type: req.body.method_type,
is_default: false,
data: req.body.data,
};
await paymentService.savePaymentMethod(paymentData);
res.status(httpStatus.OK).send();
});
export const deletePaymentMethod = catchAsync(async (req: RDeletePaymentMethod, res) => {
await paymentService.deletePaymentMethod({
username: req.params?.username,
method_id: req.body.method_id,
});
res.status(httpStatus.OK).send();
});
@@ -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 '../plugins/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,12 +1,30 @@
import http from 'http-status';
import ApiError from '../utils/ApiError';
import catchAsync from '../utils/catchAsync';
import { userService, tokenService } from '../services';
import { 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 { IUser } 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 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 }; // Возвращаем объект в ответ
// }
// }
/**
* Порядок регистрации:
@@ -19,10 +37,11 @@ import { IUser } from '../models/user.model';
* 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);
@@ -30,54 +49,56 @@ 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']);
Object.keys(filter).forEach((key) => {
if (filter[key] !== undefined) {
filter[key] = { $eq: filter[key], $ne: 'service' };
} else {
filter[key] = { $ne: 'service' };
}
});
const users = await userService.queryUsers(filter, options);
const result = {} as IGetResponse<IUser>;
//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;
result.totalPages = users.totalPages;
result.totalResults = users.totalResults;
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) => {
@@ -89,6 +110,6 @@ export const updateUser = catchAsync(async (req, res) => {
});
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);
}
}
}
+3 -70
View File
@@ -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
View File
@@ -3,7 +3,7 @@ import config from '../config/config';
const swaggerDef = {
openapi: '3.0.0',
info: {
title: "API Цифрового Кооператива",
title: 'API Цифрового Кооператива',
// description: `Модуль предоставляет:
// - API для хранения персональных данных пользователей
+61
View File
@@ -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();
}
});
+27 -10
View File
@@ -1,30 +1,43 @@
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/document.service';
import { connectGenerator, diconnectGenerator } from './services/document.service';
import { initSocketConnection } from './controllers/ws.controller';
import { pluginFactory } from './plugins/pluginFactory';
import { paymentService } from './services';
import { initializeDefaultPlugins } from './services/plugin.service';
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-серверу
// подключаемся к 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 +48,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();
}
+20 -7
View File
@@ -6,19 +6,32 @@ 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 (!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) || //для аудентификации по username в params
(req.body.username && req.body.username === user.username) || //для аудентификации по username в body
(req.body.data && req.body.data.username && req.body.data.username === user.username); //для аудентификации при генерации документов
if (!isOwnResource && (!hasRequiredRights || !requiredRights.length)) {
return reject(new ApiError(FORBIDDEN, 'Недостаточно прав доступа'));
}
resolve();
+1 -3
View File
@@ -40,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;

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