Compare commits

...

250 Commits

Author SHA1 Message Date
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
Alex Ant 3608e7601f chore(release): publish
- cooparser-ts@1.0.17
 - coopback@1.7.27
 - coopdoc-generator-ts@1.0.72
 - cooptypes@1.0.17
 - coop-notificator@0.0.21
 - terminal@0.1.30
2024-07-25 19:41:18 +05:00
Alex Ant a75cfa1056 chore(release): publish
- coopback@1.7.27-alpha.5
 - terminal@0.1.30-alpha.4
2024-07-25 19:39:41 +05:00
Alex Ant f08c4102fd login with email 2024-07-25 19:37:13 +05:00
Alex Ant 9bfd7c7d9a chore(release): publish
- coopback@1.7.27-alpha.4
2024-07-25 18:24:15 +05:00
Alex Ant a359b4fbdb fix 2024-07-25 18:22:17 +05:00
Alex Ant effb6df495 chore(release): publish
- cooparser-ts@1.0.17-alpha.2
 - coopback@1.7.27-alpha.3
 - coopdoc-generator-ts@1.0.72-alpha.3
 - cooptypes@1.0.17-alpha.3
 - coop-notificator@0.0.21-alpha.3
 - terminal@0.1.30-alpha.3
2024-07-25 17:56:22 +05:00
Alex Ant 6e6c963663 update 2024-07-25 17:55:36 +05:00
Alex Ant 23f5c7b578 update 2024-07-25 17:55:14 +05:00
Alex Ant 02b223266d pre 2024-07-25 17:05:44 +05:00
Alex Ant 745a8c99e1 publish cooptypes 2024-07-25 17:03:17 +05:00
Alex Ant 5532038fd8 coopback payment methods (list | add | del) & tests 2024-07-25 16:06:06 +05:00
Alex Ant 3be31c5240 coopback tests work 2024-07-24 23:30:01 +05:00
Alex Ant 11ab59e109 coopback tests restore #1 2024-07-23 22:34:42 +05:00
Alex Ant d5bbf10041 before generator refactoring 2024-07-23 10:10:22 +05:00
Alex Ant ba4f9581a4 before generator refactoring 2024-07-23 09:46:19 +05:00
Alex Ant 52eabab633 chore(release): publish
- coopback@1.7.27-alpha.0
 - coopdoc-generator-ts@1.0.72-alpha.0
 - cooptypes@1.0.17-alpha.0
 - coop-notificator@0.0.21-alpha.0
 - terminal@0.1.30-alpha.0
2024-07-18 20:15:52 +05:00
Alex Ant 600949b784 изменяемые публичные контакты кооператива 2024-07-18 20:09:46 +05:00
Alex Ant dcca48097d chore(members): убран лишний код 2024-07-16 21:21:06 +05:00
Alex Ant 6e20c32a14 update 2024-07-16 21:13:51 +05:00
715 changed files with 36504 additions and 33992 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,
}
+100
View File
@@ -0,0 +1,100 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.1.3](https://github.com/coopenomics/monocoop/compare/v2.1.2-alpha.10...v2.1.3) (2024-10-26)
## [2.1.2](https://github.com/coopenomics/monocoop/compare/v2.1.1...v2.1.2) (2024-10-19)
**Note:** Version bump only for package monocoop
## [2.1.2](https://github.com/coopenomics/monocoop/compare/v2.1.1...v2.1.2) (2024-10-19)
**Note:** Version bump only for package monocoop
# [2.1.0](https://github.com/coopenomics/monocoop/compare/v2.0.10-alpha.3...v2.1.0) (2024-10-13)
### Features
* запрос соглашений ([9a6a72f](https://github.com/coopenomics/monocoop/commit/9a6a72f605ba52eef2ed6f18ccee6fbed287ea00))
## [2.0.9](https://github.com/coopenomics/monocoop/compare/v2.0.8...v2.0.9) (2024-10-10)
**Note:** Version bump only for package monocoop
## [2.0.8](https://github.com/coopenomics/monocoop/compare/v2.0.7...v2.0.8) (2024-10-09)
**Note:** Version bump only for package monocoop
## [2.0.7](https://github.com/coopenomics/monocoop/compare/v2.0.6...v2.0.7) (2024-09-30)
**Note:** Version bump only for package monocoop
## [2.0.6](https://github.com/coopenomics/monocoop/compare/v2.0.5...v2.0.6) (2024-09-30)
**Note:** Version bump only for package monocoop
## [2.0.5](https://github.com/coopenomics/monocoop/compare/v2.0.5-alpha.0...v2.0.5) (2024-09-30)
**Note:** Version bump only for package monocoop
## [2.0.2](https://github.com/coopenomics/monocoop/compare/v2.0.2-alpha.1...v2.0.2) (2024-09-29)
**Note:** Version bump only for package monocoop
# 2.1.0 (2024-09-29)
### Bug Fixes
* **importers:** исправлена ошибка в импорте модуля @wharfkit/contract ([bf89032](https://github.com/coopenomics/monocoop/commit/bf89032d8f66444804a2521f4eb96ffc75b0f605))
* **terminal:** исправлен шрифт в README ([889447a](https://github.com/coopenomics/monocoop/commit/889447a93ceadb577613ddb5b1cb2ef1cc3d54b6))
* **terminal:** исправлено описание проекта ([989b318](https://github.com/coopenomics/monocoop/commit/989b3180ded99a871e018cf26e6c493449223c01))
* **terminal:** fix typo in README.md ([fdf9996](https://github.com/coopenomics/monocoop/commit/fdf999619d2d69e960b062fe6815f5b057c95f48))
### Features
* добавлен docker-compose.yaml ([6a46697](https://github.com/coopenomics/monocoop/commit/6a46697c9d6cc3cde14dbce8f70997c00f9850de))
* **package:** добавлен скрипт gpt-commit для удобного коммита ([e0b5107](https://github.com/coopenomics/monocoop/commit/e0b510799bb0ac68890d572deb652beefd0651c4))
* **terminal:** добавлена поддержка новых команд ([73e4ca2](https://github.com/coopenomics/monocoop/commit/73e4ca226acebcbea3ae59a62def99d86efb1353))
+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.
+76
View File
@@ -3,6 +3,82 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.1.3](https://github.com/copenomics/cooparser/compare/v2.1.2-alpha.10...v2.1.3) (2024-10-26)
## [2.1.2](https://github.com/copenomics/cooparser/compare/v2.1.1...v2.1.2) (2024-10-19)
**Note:** Version bump only for package cooparser-ts
## [2.1.2](https://github.com/copenomics/cooparser/compare/v2.1.1...v2.1.2) (2024-10-19)
**Note:** Version bump only for package cooparser-ts
# [2.1.0](https://github.com/copenomics/cooparser/compare/v2.0.10-alpha.3...v2.1.0) (2024-10-13)
**Note:** Version bump only for package cooparser-ts
## [2.0.9](https://github.com/copenomics/cooparser/compare/v2.0.8...v2.0.9) (2024-10-10)
**Note:** Version bump only for package cooparser-ts
## [2.0.8](https://github.com/copenomics/cooparser/compare/v2.0.7...v2.0.8) (2024-10-09)
**Note:** Version bump only for package cooparser-ts
## [2.0.2](https://github.com/copenomics/cooparser/compare/v2.0.2-alpha.1...v2.0.2) (2024-09-29)
**Note:** Version bump only for package cooparser-ts
## [1.0.20](https://github.com/copenomics/cooparser/compare/cooparser-ts@1.0.20-alpha.0...cooparser-ts@1.0.20) (2024-09-29)
**Note:** Version bump only for package cooparser-ts
## [1.0.18](https://github.com/copenomics/cooparser/compare/cooparser-ts@1.0.17...cooparser-ts@1.0.18) (2024-08-25)
**Note:** Version bump only for package cooparser-ts
## [1.0.17](https://github.com/copenomics/cooparser/compare/cooparser-ts@1.0.17-alpha.2...cooparser-ts@1.0.17) (2024-07-25)
**Note:** Version bump only for package cooparser-ts
## [1.0.16](https://github.com/copenomics/cooparser/compare/cooparser-ts@1.0.16-alpha.0...cooparser-ts@1.0.16) (2024-07-15)
**Note:** Version bump only for package cooparser-ts
+2 -3
View File
@@ -1,12 +1,12 @@
{
"name": "pkg-placeholder",
"version": "1.0.16",
"version": "2.1.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "pkg-placeholder",
"version": "1.0.16",
"version": "2.1.3",
"license": "MIT",
"devDependencies": {
"@antfu/eslint-config": "^2.16.0",
@@ -16,7 +16,6 @@
"bumpp": "^9.4.0",
"eslint": "^8.57.0",
"esno": "^4.7.0",
"lint-staged": "^15.2.2",
"pnpm": "^8.15.7",
"rimraf": "^5.0.5",
"simple-git-hooks": "^2.11.1",
+4 -9
View File
@@ -1,8 +1,8 @@
{
"name": "cooparser-ts",
"type": "module",
"version": "1.0.16",
"private": true,
"version": "2.1.3",
"private": false,
"packageManager": "pnpm@9.0.6",
"description": "",
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
@@ -41,6 +41,7 @@
"deploy-production": "git checkout production && git merge testnet && git push origin production && git checkout main",
"build": "unbuild",
"dev": "concurrently -n 'PARSER' -c 'bgBlue.white' \"nodemon --watch src --ext ts,js,env --exec 'esno' src/index.ts\"",
"dev:test": "NODE_ENV=test concurrently -n 'PARSER' -c 'bgBlue.white' \"nodemon --watch src --ext ts,js,env --exec 'esno' src/index.ts\"",
"lint": "eslint .",
"prepublishOnly": "nr build",
"release": "bumpp && npm publish",
@@ -72,7 +73,6 @@
"concurrently": "^8.2.2",
"eslint": "^8.57.0",
"esno": "^4.7.0",
"lint-staged": "^15.2.2",
"nodemon": "^3.1.4",
"pnpm": "^8.15.7",
"rimraf": "^5.0.5",
@@ -83,10 +83,5 @@
"vite": "^5.2.10",
"vitest": "^1.5.2"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
},
"lint-staged": {
"*": "eslint --fix"
}
"gitHead": "7b59ea4b96991ed06abdc92e6d7835632845e3a5"
}
+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)
})
}
+8 -10
View File
@@ -16,7 +16,7 @@ export class Database {
async connect() {
await this.client.connect()
this.db = this.client.db('cooperative')
this.db = this.client.db()
this.actions = this.db.collection('actions')
this.deltas = this.db.collection('deltas')
this.sync = this.db.collection('sync')
@@ -51,18 +51,15 @@ export class Database {
const pipeline = [
{ $match: filter },
{ $sort: { block_num: -1, _id: -1 } },
{ $sort: { block_num: -1 } }, // Сортировка по primary_key и block_num
{ $group: { _id: '$primary_key', doc: { $first: '$$ROOT' } } },
// { $match: { 'doc.present': true } },
{ $replaceRoot: { newRoot: '$doc' } },
{ $sort: { block_num: -1 } },
{ $skip: (page - 1) * limit }, // Применяется внутри пайплайна
{ $limit: limit }, // Применяется внутри пайплайна
]
const [result] = await Promise.all([
this.deltas.aggregate(pipeline)
.skip((page - 1) * limit)
.limit(limit)
.toArray(),
])
const result = await this.deltas.aggregate(pipeline).toArray()
return {
results: result as IDelta[],
@@ -79,9 +76,10 @@ export class Database {
const result = await this.actions.aggregate([
{ $match: query },
{ $sort: { block_num: -1, _id: -1 } },
{ $sort: { block_num: -1 } }, // Сортировка по primary_key и block_num
{ $group: { _id: '$global_sequence', doc: { $first: '$$ROOT' } } },
{ $replaceRoot: { newRoot: '$doc' } },
{ $sort: { block_num: -1 } },
{ $skip: (page - 1) * limit },
{ $limit: limit },
]).toArray()
+3 -2
View File
@@ -10,10 +10,10 @@ function getEnvVar(key: string): string {
return envVar
}
export const node_env = getEnvVar('NODE_ENV')
export const eosioApi = getEnvVar('API')
export const shipApi = getEnvVar('SHIP')
export const mongoUri = getEnvVar('MONGO_EXPLORER_URI')
export const mongoUri = `${getEnvVar('MONGO_EXPLORER_URI')}${node_env === 'test' ? '-test' : ''}`
export const startBlock = getEnvVar('START_BLOCK')
export const finishBlock = getEnvVar('FINISH_BLOCK')
export const redisPort = getEnvVar('REDIS_PORT')
@@ -54,6 +54,7 @@ export const subsribedActions: IActionConfig[] = [
{ code: 'soviet', action: 'joincoopdec' },
{ code: 'soviet', action: 'updateboard', notify: true },
{ code: 'soviet', action: 'createboard', notify: true },
]
// --------------------------
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"
}
]
}
}
+29
View File
@@ -0,0 +1,29 @@
{
"env": {
"node": true,
"jest": true,
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"plugins": ["@typescript-eslint", "prettier"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"no-console": "warn",
"func-names": "off",
"no-underscore-dangle": "off",
"consistent-return": "off",
"jest/expect-expect": "off",
"security/detect-object-injection": "off",
"@typescript-eslint/no-unused-vars": "warn"
}
}
+4 -1
View File
@@ -12,4 +12,7 @@ yarn-error.log
coverage
.env-mixed
.env-local
.env-local
logs
logs/*
+2 -2
View File
@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# . "$(dirname "$0")/_/husky.sh"
yarn install
# yarn install
+2 -2
View File
@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# . "$(dirname "$0")/_/husky.sh"
git status
# git status
+2 -2
View File
@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# . "$(dirname "$0")/_/husky.sh"
yarn lint-staged
# yarn lint-staged
+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"
}
+140
View File
@@ -3,6 +3,146 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.1.3](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.2-alpha.10...v2.1.3) (2024-10-26)
## [2.1.2](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.1...v2.1.2) (2024-10-19)
**Note:** Version bump only for package @coopenomics/controller
## [2.1.2](https://github.com/hagopj13/node-express-boilerplate/compare/v2.1.1...v2.1.2) (2024-10-19)
**Note:** Version bump only for package coopback
# [2.1.0](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.10-alpha.3...v2.1.0) (2024-10-13)
**Note:** Version bump only for package coopback
## [2.0.8](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.7...v2.0.8) (2024-10-09)
**Note:** Version bump only for package coopback
## [2.0.7](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.6...v2.0.7) (2024-09-30)
**Note:** Version bump only for package coopback
## [2.0.6](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.5...v2.0.6) (2024-09-30)
**Note:** Version bump only for package coopback
## [2.0.5](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.5-alpha.0...v2.0.5) (2024-09-30)
**Note:** Version bump only for package coopback
## [2.0.2](https://github.com/hagopj13/node-express-boilerplate/compare/v2.0.2-alpha.1...v2.0.2) (2024-09-29)
**Note:** Version bump only for package coopback
## [1.7.38](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.38-alpha.3...coopback@1.7.38) (2024-09-29)
**Note:** Version bump only for package coopback
## [1.7.34](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.33...coopback@1.7.34) (2024-08-25)
**Note:** Version bump only for package coopback
## [1.7.33](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.32...coopback@1.7.33) (2024-08-25)
**Note:** Version bump only for package coopback
## [1.7.32](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.31...coopback@1.7.32) (2024-08-25)
**Note:** Version bump only for package coopback
## [1.7.31](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.31-alpha.1...coopback@1.7.31) (2024-08-25)
**Note:** Version bump only for package coopback
## [1.7.30](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.29...coopback@1.7.30) (2024-08-13)
**Note:** Version bump only for package coopback
## [1.7.29](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.29-alpha.0...coopback@1.7.29) (2024-08-05)
**Note:** Version bump only for package coopback
## [1.7.28](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.28-alpha.0...coopback@1.7.28) (2024-07-26)
**Note:** Version bump only for package coopback
## [1.7.27](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.27-alpha.5...coopback@1.7.27) (2024-07-25)
**Note:** Version bump only for package coopback
## [1.7.26](https://github.com/hagopj13/node-express-boilerplate/compare/coopback@1.7.26-alpha.0...coopback@1.7.26) (2024-07-15)
**Note:** Version bump only for package coopback
+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 -5
View File
@@ -1,12 +1,12 @@
{
"name": "create-nodejs-express-app",
"version": "1.7.26",
"version": "2.1.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "create-nodejs-express-app",
"version": "1.7.26",
"version": "2.1.3",
"license": "MIT",
"dependencies": {
"@a2seven/yoo-checkout": "^1.1.4",
@@ -32,7 +32,7 @@
"jsonwebtoken": "^8.5.1",
"jspdf": "^2.5.1",
"moment": "^2.24.0",
"mongoose": "^5.7.7",
"mongoose": "^8.5.2",
"morgan": "^1.9.1",
"nodemailer": "^6.3.1",
"nunjucks": "^3.2.4",
@@ -54,14 +54,13 @@
"@types/express": "^4.17.21",
"@types/node": "^20.11.26",
"coveralls": "^3.0.7",
"eslint": "^7.0.0",
"eslint": "^7.32.0",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^24.0.1",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-security": "^1.4.0",
"lint-staged": "^11.0.0",
"node-mocks-http": "^1.8.0",
"nodemon": "^2.0.0",
"prettier": "^2.0.5",
+26 -8
View File
@@ -1,22 +1,26 @@
{
"name": "coopback",
"version": "1.7.26",
"name": "@coopenomics/controller",
"version": "2.1.3",
"description": "",
"private": true,
"private": false,
"bin": "bin/createNodejsApp.js",
"main": "src/index.ts",
"repository": "https://github.com/hagopj13/node-express-boilerplate.git",
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
"license": "MIT",
"types": "src/types/index.ts",
"engines": {
"node": ">=12.0.0"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"deploy-testnet": "git checkout testnet && git merge main && git push origin testnet && git checkout main",
"deploy-production": "git checkout production && git merge testnet && git push origin production && git checkout main",
"start": "ts-node src/index.ts",
"dev": "concurrently -n 'CONTROLLER' -c 'bgCyan.black' \"nodemon --watch src --ext ts,js,env --exec 'ts-node' src/index.ts\"",
"test": "jest -i --colors --verbose --detectOpenHandles",
"test": "jest -i --colors --testTimeout=120000",
"test:watch": "jest -i --watchAll",
"coverage": "jest -i --coverage",
"coverage:coveralls": "jest -i --coverage --coverageReporters=text-lcov | coveralls",
@@ -28,9 +32,11 @@
"docker:dev": "docker-compose -f docker-compose.yml -f docker-compose.dev.yml up",
"docker:test": "docker-compose -f docker-compose.yml -f docker-compose.test.yml up",
"types": "ts-node scripts/types.ts",
"init": "ts-node scripts/init.ts",
"swagger": "ts-node scripts/swagger.ts",
"typecheck": "tsc --noEmit",
"swagger-generate": "swagger-ui-watcher ./src/docs/swagger.json"
"docs-watch": "swagger-ui-watcher ./src/docs/swagger.json",
"docs": "ts-node scripts/swagger.ts && redoc-cli bundle ./src/docs/swagger.json -o ./docs/index.html"
},
"keywords": [
"node",
@@ -78,16 +84,21 @@
"helmet": "^4.1.0",
"html-pdf-node": "^1.0.8",
"http-status": "^1.4.0",
"ioredis": "^5.4.1",
"ip": "^2.0.1",
"isomorphic-fetch": "^3.0.0",
"jest": "^29.7.0",
"joi": "^17.13.1",
"joi-to-json": "^4.3.0",
"joi-to-typescript": "^4.13.0",
"jsonwebtoken": "^8.5.1",
"jspdf": "^2.5.1",
"lodash": "^4.17.21",
"moment": "^2.24.0",
"mongoose": "^5.7.7",
"mongoose": "^8.5.2",
"mongoose-sequence": "^6.0.1",
"morgan": "^1.9.1",
"node-cron": "^3.0.3",
"nodemailer": "^6.3.1",
"nunjucks": "^3.2.4",
"passport": "^0.4.0",
@@ -97,26 +108,33 @@
"socket.io-client": "^4.7.5",
"swagger-jsdoc": "^6.0.8",
"swagger-ui-express": "^4.1.6",
"threads": "^1.7.0",
"ts-jest": "^29.1.3",
"ts-node": "^10.9.2",
"tsd": "^0.31.0",
"tsoa": "^6.4.0",
"validator": "^13.0.0",
"winston": "^3.2.1",
"winston-daily-rotate-file": "^5.0.0",
"xss-clean": "^0.1.1"
},
"devDependencies": {
"@antfu/eslint-config": "^2.24.1",
"@redocly/cli": "^1.18.0",
"@types/express": "^4.17.21",
"@types/faker": "^6.6.9",
"@types/node": "^20.11.26",
"@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^8.0.0",
"concurrently": "^8.2.2",
"coveralls": "^3.0.7",
"eslint": "^7.0.0",
"eslint": "^7.32.0",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^24.0.1",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-security": "^1.4.0",
"lint-staged": "^11.0.0",
"node-mocks-http": "^1.8.0",
"nodemon": "^2.0.0",
"prettier": "^2.0.5",
@@ -1,13 +1,14 @@
import config from '../src/config/config';
import logger from '../src/config/logger';
import { User } from '../src/models';
import { tokenService } from '../src/services';
import userService from '../src/services/user.service';
import { userService } from '../src/services';
import mongoose from 'mongoose';
const args = process.argv.slice(2);
if (args.length < 1) {
console.error('Ошибка: Необходимо указать имя пользователя');
logger.error('Ошибка: Необходимо указать имя пользователя');
process.exit(1);
}
@@ -16,12 +17,11 @@ const [username] = args;
async function createServiceUser(username: string) {
try {
await mongoose.connect(config.mongoose.url, config.mongoose.options);
const user = await userService.createServiceUser(username);
const token = await tokenService.generateServiceAccessToken(user);
console.log('token:', token.access.token);
} catch (e: any) {
console.log('Ошибка: ', e.message);
logger.error('Ошибка: ', e.message);
}
process.exit(0);
+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) {
+95
View File
@@ -0,0 +1,95 @@
import config from '../src/config/config';
import { User } from '../src/models';
import { tokenService } from '../src/services';
import { userService } from '../src/services';
import mongoose from 'mongoose';
import { connectGenerator } from '../src/services/document.service';
import crypto from 'crypto';
import bcryptjs from 'bcryptjs';
const { compare, hash } = bcryptjs;
async function init() {
try {
await connectGenerator();
await mongoose.connect(config.mongoose.url, config.mongoose.options);
const wif = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3';
await userService.createUser({
username: 'ant',
email: 'dacom.dark.sun@gmail.com',
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
role: 'user',
type: 'individual',
individual_data: {
first_name: 'Иван',
last_name: 'Иванов',
middle_name: 'Иванович',
birthdate: '2000-01-01',
phone: '+1234567890',
email: 'ivanov.ivan@example.com',
full_address: 'Переулок Иванов д. 1',
},
});
const user = await userService.getUserByUsername('ant');
user.status = 'active';
user.is_registered = true;
await user.save();
const voskhod = await userService.createUser({
username: 'voskhod',
email: 'chairman.voskhod@gmail.com',
public_key: 'EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV',
role: 'user',
type: 'organization',
organization_data: {
is_cooperative: true,
type: 'coop',
short_name: '"ПК ТЕСТ-Восход"',
full_name: 'Потребительский Кооператив "ТЕСТ-ВОСХОД"',
represented_by: {
first_name: 'Иван',
last_name: 'Иванов',
middle_name: 'Иванович',
position: 'Председатель',
based_on: 'Решения общего собрания №1',
},
country: 'Russia',
city: 'Москва',
full_address: 'Переулок Правды, дом 1',
email: 'chairman.voskhod@gmail.com',
phone: '+77077770707',
details: {
inn: '1234567890',
ogrn: '1234567890123',
},
bank_account: {
account_number: '40817810099910004312',
currency: 'RUB',
card_number: '1234567890123456',
bank_name: 'Sberbank',
details: {
bik: '123456789',
corr: '30101810400000000225',
kpp: '123456789',
},
},
},
});
const coop = await userService.getUserByUsername('voskhod');
coop.status = 'active';
coop.is_registered = true;
await coop.save();
console.log('ok');
} catch (e: any) {
console.log('Ошибка: ', e);
}
process.exit(0);
}
init();
@@ -0,0 +1,43 @@
import config from '../../src/config/config';
import { User } from '../../src/models';
import { tokenService } from '../../src/services';
import { userService } from '../../src/services';
import mongoose from 'mongoose';
import { connectGenerator } from '../../src/services/document.service';
import crypto from 'crypto';
import bcryptjs from 'bcryptjs';
const { compare, hash } = bcryptjs;
async function init() {
try {
await connectGenerator();
await mongoose.connect(config.mongoose.url, config.mongoose.options);
const orgs_collection = mongoose.connection.collection('OrgData');
const bankData_collection = mongoose.connection.collection('PaymentData');
const orgs = await orgs_collection.find().toArray();
for (const org of orgs) {
await bankData_collection.insertOne({
_created_at: org._created_at,
username: org.username,
method_id: 1,
user_type: 'organization',
method_type: 'bank_transfer',
is_default: true,
data: org.bank_account,
deleted: false,
block_num: org.block_num,
});
}
} catch (e: any) {
console.log('Ошибка: ', e);
}
process.exit(0);
}
init();
+4 -4
View File
@@ -3,12 +3,12 @@ import crypto from 'crypto';
const { compare, hash } = bcryptjs;
async function generate() {
const wif = '';
const wif = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3';
const hash_from_wif = await crypto.createHash('sha256').update(wif).digest('hex');
const password = await hash(hash_from_wif, 8)
console.log(password)
console.log('hash_from_wif: ', hash_from_wif);
const password = await hash(hash_from_wif, 8);
console.log(password);
}
generate();
+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.
-10
View File
@@ -14,16 +14,6 @@ import routes from './routes/v1/index';
import { errorConverter, errorHandler } from './middlewares/error';
import ApiError from './utils/ApiError';
import { connectGenerator } from './services/data.service';
// Подключите генератор
connectGenerator()
.then(() => {
console.log('Generator connected');
})
.catch((error) => {
console.error('Failed to connect generator:', error);
});
const app = express();
if (config.env !== 'test') {
+14 -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,16 @@ const envVarsSchema = Joi.object()
JWT_VERIFY_EMAIL_EXPIRATION_MINUTES: Joi.number()
.default(10)
.description('minutes after which verify email token expires'),
JWT_INVITE_EXPIRATION_MINUTES: Joi.number().default(3600).description('minutes after invite token expires'),
SMTP_HOST: Joi.string().description('server that will send the emails'),
SMTP_PORT: Joi.number().description('port to connect to the email server'),
SMTP_USERNAME: Joi.string().description('username for email server'),
SMTP_PASSWORD: Joi.string().description('password for email server'),
EMAIL_FROM: Joi.string().description('the from field in the emails sent by the app'),
COOPNAME: Joi.string().description('имя аккаунта кооператива'),
})
.unknown();
@@ -39,13 +41,15 @@ if (error) {
export default {
env: envVars.NODE_ENV,
base_url: envVars.BASE_URL,
port: envVars.PORT,
server_secret: envVars.SERVER_SECRET,
mongoose: {
url: envVars.MONGODB_URL + (envVars.NODE_ENV === 'test' ? '-test' : ''),
options: {
useCreateIndex: true,
useNewUrlParser: true,
useUnifiedTopology: true,
// useCreateIndex: true,
// useNewUrlParser: true,
// useUnifiedTopology: true,
},
},
jwt: {
@@ -54,6 +58,7 @@ export default {
refreshExpirationDays: envVars.JWT_REFRESH_EXPIRATION_DAYS,
resetPasswordExpirationMinutes: envVars.JWT_RESET_PASSWORD_EXPIRATION_MINUTES,
verifyEmailExpirationMinutes: envVars.JWT_VERIFY_EMAIL_EXPIRATION_MINUTES,
inviteExpirationMinutes: envVars.JWT_INVITE_EXPIRATION_MINUTES,
},
email: {
smtp: {
@@ -66,4 +71,5 @@ export default {
},
from: envVars.EMAIL_FROM,
},
coopname: envVars.COOPNAME,
};
+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);
}
+29 -4
View File
@@ -1,8 +1,33 @@
const user = [
'getSelf',
'initialPayment',
'getMyDocuments',
'getMyOrders',
'sendVerificationEmail',
'manageMyMethods',
'createDeposit',
'joinCooperative',
'getVars',
];
const member = [
...user,
'getUsers',
'manageUsers',
'loadAgenda',
'loadStaff',
'getDocuments',
'loadInfo',
'manageOrders',
'regenerateDocument',
];
const chairman = [...user, ...member, 'addUser', 'setVars', 'manageSettings', 'setWif', 'setPlugin'];
const allRoles = {
user: [''],
service: ['sendNotification'],
chairman: ['getUsers', 'manageUsers', 'loadAgenda', 'loadStaff', 'getDocuments', 'loadInfo'],
member: ['getUsers', 'manageUsers', 'loadAgenda', 'loadStaff', 'getDocuments', 'loadInfo'],
user,
member,
chairman,
};
export const roles = Object.keys(allRoles);
+1
View File
@@ -3,4 +3,5 @@ export const tokenTypes = {
REFRESH: 'refresh',
RESET_PASSWORD: 'resetPassword',
VERIFY_EMAIL: 'verifyEmail',
INVITE: 'invite',
};
@@ -1,16 +1,15 @@
import httpStatus from 'http-status';
import catchAsync from '../utils/catchAsync';
import { authService, userService, tokenService, emailService, blockchainService } from '../services';
import { authService, tokenService, emailService } from '../services';
import { RForgotKey, RRefreshTokens, RResetKey, RVerifyEmail } from '../types';
const { CREATED, NO_CONTENT } = httpStatus;
const { NO_CONTENT } = httpStatus;
export const login = catchAsync(async (req, res) => {
const { username, password } = req.body;
const user = await authService.loginUserWithUsernameAndPassword(username, password);
const { now, signature, email } = req.body;
if (user.is_registered === false)
throw new Error('Регистрация не была завершена. Пожалуйста, подождите решения совета или начните с начала. ')
const user = await authService.loginUserWithSignature(email, now, signature);
const tokens = await tokenService.generateAuthTokens(user);
res.send({ user, tokens });
@@ -21,19 +20,19 @@ export const logout = catchAsync(async (req, res) => {
res.status(NO_CONTENT).send();
});
export const refreshTokens = catchAsync(async (req, res) => {
const tokens = await authService.refreshAuth(req.body.refreshToken);
res.send({ ...tokens });
export const refreshTokens = catchAsync(async (req: RRefreshTokens, res: any) => {
const tokens = await authService.refreshAuth(req.body);
res.send(tokens);
});
export const forgotPassword = catchAsync(async (req, res) => {
const resetPasswordToken = await tokenService.generateResetPasswordToken(req.body.email);
await emailService.sendResetPasswordEmail(req.body.email, resetPasswordToken);
export const lostKey = catchAsync(async (req: RForgotKey, res) => {
const resetKeyToken = await tokenService.generateResetKeyToken(req.body.email);
await emailService.sendResetKeyEmail(req.body.email, resetKeyToken);
res.status(NO_CONTENT).send();
});
export const resetPassword = catchAsync(async (req, res) => {
await authService.resetPassword(req.query.token, req.body.password);
export const resetKey = catchAsync(async (req: RResetKey, res) => {
await authService.resetKey(req.body.token, req.body.public_key);
res.status(NO_CONTENT).send();
});
@@ -43,7 +42,7 @@ export const sendVerificationEmail = catchAsync(async (req, res) => {
res.status(NO_CONTENT).send();
});
export const verifyEmail = catchAsync(async (req, res) => {
export const verifyEmail = catchAsync(async (req: RVerifyEmail, res) => {
await authService.verifyEmail(req.query.token);
res.status(NO_CONTENT).send();
});
@@ -1,36 +1,34 @@
import catchAsync from '../utils/catchAsync';
import { coopService, dataService } from '../services';
import { coopService, documentService } from '../services';
import { Cooperative } from 'cooptypes';
import { generator } from '../services/data.service';
import ApiError from '../utils/ApiError';
import httpStatus from 'http-status';
import { Request, Response } from 'express';
export const loadInfo = catchAsync(async (req, res) => {
export const loadInfo = catchAsync(async (req: Request, res: Response) => {
const cooperative = await coopService.loadInfo(String(process.env.COOPNAME));
res.send(cooperative);
});
export const loadContacts = catchAsync(async (req, res) => {
export const loadContacts = catchAsync(async (req: Request, res: Response) => {
const contacts = await coopService.loadContacts(String(process.env.COOPNAME));
res.send(contacts);
});
export const loadAgenda = catchAsync(async (req, res) => {
export const loadAgenda = catchAsync(async (req: Request, res: Response) => {
const { coopname } = req.query;
const agenda = await coopService.loadAgenda(coopname);
const agenda = await coopService.loadAgenda(coopname as string);
const complexAgenda: Cooperative.Documents.IComplexAgenda[] = [];
const complexAgenda: Cooperative.Document.IComplexAgenda[] = [];
for (const { action, table } of agenda) {
const documents = await dataService.buildComplexDocument(action);
const documents = await documentService.buildComplexDocument(action);
if (documents.statement.document) complexAgenda.push({ documents, action, table });
}
res.send(complexAgenda);
});
export const loadStaff = catchAsync(async (req, res) => {
export const loadStaff = catchAsync(async (req, res: Response) => {
const { coopname } = req.query;
const staff = await coopService.loadStaff(coopname);
res.send(staff);
@@ -1,22 +0,0 @@
import catchAsync from '../utils/catchAsync';
import { dataService } from '../services/index';
import type { IGetDocuments, RGenerate, RGetDocuments } from '../types';
import httpStatus from 'http-status';
import pick from '../utils/pick';
import { IGetResponse } from '../types/common';
export const generateDocument = catchAsync(async (req: RGenerate, res) => {
const document = await dataService.generateDocument(req.body);
res.status(httpStatus.CREATED).send(document);
});
export const getDocuments = catchAsync(async (req: RGetDocuments, res) => {
let filter = req.query?.filter
let limit = req.query?.limit as number
let page = req.query?.page as number
const documents: IGetResponse = await dataService.queryDocuments(filter, page, limit);
res.status(httpStatus.OK).send(documents);
});
@@ -0,0 +1,21 @@
import catchAsync from '../utils/catchAsync';
import { documentService } from '../services/index';
import type { RGenerate, RGetDocuments } from '../types';
import httpStatus from 'http-status';
export const generateDocument = catchAsync(async (req: RGenerate, res) => {
const document = await documentService.generateDocument(req.body);
res.status(httpStatus.CREATED).send(document);
});
export const getDocuments = catchAsync(async (req: RGetDocuments, res) => {
const filter = req.query?.filter;
const limit = req.query?.limit as number;
const page = req.query?.page as number;
const type = req.query?.type as string;
const documents = await documentService.queryDocuments(type, filter, page, limit);
res.status(httpStatus.OK).send(documents);
});
+6 -1
View File
@@ -1,6 +1,11 @@
export * as authController from './auth.controller';
export * as userController from './user.controller';
export * as dataController from './data.controller';
export * as documentController from './document.controller';
export * as orderController from './order.controller';
export * as coopController from './coop.controller';
export * as notifyController from './notify.controller';
export * as systemController from './system.controller';
export * as participantController from './participant.controller';
export * as methodController from './method.controller';
export * as settingController from './setting.controller';
export * as pluginController from './plugin.controller';
@@ -0,0 +1,55 @@
import http from 'http-status';
import catchAsync from '../utils/catchAsync';
import { methodService } from '../services';
import { IGetResponse } from '../types/common';
import ApiError from '../utils/ApiError';
import httpStatus from 'http-status';
import { Cooperative } from 'cooptypes';
import { getUserByUsername } from '../services/user.service';
import type { RDeletePaymentMethod, RGetListPaymentMethods, RSavePaymentMethod } from '../types';
export const listPaymentMethods = catchAsync(async (req: RGetListPaymentMethods, res) => {
const filter = req.params?.username ? { username: req.params.username } : {};
const methods: IGetResponse<any> = await methodService.listPaymentMethods({ ...filter });
res.status(httpStatus.OK).send(methods);
});
export const addPaymentMethod = catchAsync(async (req: RSavePaymentMethod, res) => {
if (req.params?.username != req.body.username)
throw new ApiError(httpStatus.BAD_REQUEST, 'username в params и body должны совпадать');
const user = await getUserByUsername(req.body.username);
const method = req.body as any;
if (method.method_type === 'sbp' && !method.data.phone)
throw new ApiError(httpStatus.BAD_REQUEST, 'Не указан телефон для метода СБП');
if (method.method_type === 'bank_transfer' && !method.data.account_number)
throw new ApiError(httpStatus.BAD_REQUEST, 'Не верно указаны реквизиты для банковского платежа');
const paymentData: Cooperative.Payments.IPaymentData = {
username: req.body.username,
method_id: req.body.method_id,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
user_type: user.type as any,
method_type: req.body.method_type,
is_default: false,
data: req.body.data,
};
await methodService.savePaymentMethod(paymentData);
res.status(httpStatus.OK).send();
});
export const deletePaymentMethod = catchAsync(async (req: RDeletePaymentMethod, res) => {
await methodService.deletePaymentMethod({
username: req.params?.username,
method_id: req.body.method_id,
});
res.status(httpStatus.OK).send();
});
@@ -3,56 +3,60 @@ import catchAsync from '../utils/catchAsync';
import { orderService } from '../services';
import { ICreateDeposit, ICreateInitialPayment } from '../types';
import { Request, Response } from 'express';
import * as ip from 'ip';
// Список разрешенных IP-адресов
// const allowedIPs = [
// '185.71.76.0/27',
// '185.71.77.0/27',
// '77.75.153.0/25',
// '77.75.156.11',
// '77.75.156.35',
// '77.75.154.128/25',
// '2a02:5180::/32'
// ];
// if (process.env.NODE_ENV === 'development')
// allowedIPs.push('127.0.0.1')
import httpStatus from 'http-status';
import { IPNProviderFactory } from '../services/payment/ipn/ipnProviderFactory';
import pick from '../utils/pick';
import logger from '../config/logger';
import mongoose from 'mongoose';
export const catchIPN = catchAsync(async (req: Request, res: Response) => {
// const ipAddr = ((req.headers['x-real-ip'] as string) || req.ip || '').replace(/^::ffff:/, '');
// console.log("ip: ", ipAddr);
const providerName = req.params.provider;
logger.info(`Recieve new IPN for provider ${providerName}`, { body: req.body, source: 'catchIPN' });
// const isAllowed = allowedIPs.some(allowedIP => {
// if (allowedIP.includes('/')) { // Если это диапазон
// return ip.cidrSubnet(allowedIP).contains(ipAddr);
// }
// return allowedIP === ipAddr; // Если это конкретный IP
// });
const provider = IPNProviderFactory.createProvider(providerName);
// if (!isAllowed) {
// console.log("попытка доступа с запрещенного IP: ", ipAddr)
// return res.status(http.FORBIDDEN).send('Доступ запрещен');
// }
// console.log("on catch ipn", req.body);
res.status(http.OK).send();
orderService.catchIPN(req.body);
// Обрабатываем IPN данные
await provider.handleIPN(req.body);
res.status(200).send();
});
export const createDeposit = catchAsync(async (req: any, res: Response) => {
const username = req.user.username
const username = req.user.username;
const result = await orderService.createDeposit(username, req.body as ICreateDeposit)
const result = await orderService.createDeposit(username, req.body as ICreateDeposit);
res.status(http.CREATED).send(result);
})
});
export const createInitialPayment = catchAsync(async (req: any, res: Response) => {
const username = req.user.username
const result = await orderService.createInitialOrder(username, req.body as ICreateInitialPayment)
const username = req.user.username;
const result = await orderService.createInitialOrder(username);
res.status(http.CREATED).send(result);
})
});
export const getOrders = catchAsync(async (req: Request, res: Response) => {
const filter = pick(req.query, ['username', 'id', 'order_num', 'status']);
const options = pick(req.query, ['sortBy', 'limit', 'page']);
const orders = await orderService.getOrders(filter, options);
res.status(httpStatus.OK).send(orders);
});
export const getMyOrders = catchAsync(async (req: Request, res: Response) => {
console.log(req.params);
const filter = pick(req.query, ['status', 'id']);
const options = pick(req.query, ['sortBy', 'limit', 'page']);
filter.username = req.params.username;
const orders = await orderService.getOrders(filter, options);
res.status(httpStatus.OK).send(orders);
});
export const setStatus = catchAsync(async (req: Request, res: Response) => {
const { status, id } = req.body;
await orderService.setStatus(id, status);
res.status(httpStatus.OK).send();
});
@@ -0,0 +1,10 @@
import httpStatus from 'http-status';
import { participantService } from '../services';
import type { RJoinCooperative } from '../types';
import catchAsync from '../utils/catchAsync';
export const joinCooperative = catchAsync(async (req: RJoinCooperative, res) => {
await participantService.joinCooperative(req.body);
res.status(httpStatus.OK).send();
});
@@ -0,0 +1,55 @@
import { Request, Response } from 'express';
import { pluginService } from '../services';
import httpStatus from 'http-status';
import type { RGetPluginConfig, RGetPluginList, RGetPluginSchema, RSetPlugin } from '../types';
import ApiError from '../utils/ApiError';
import catchAsync from '../utils/catchAsync';
import { restartWorker, terminateWorker } from '../factories/pluginFactory';
import pick from '../utils/pick';
export const getPluginList = catchAsync(async (req: RGetPluginList, res: Response) => {
const filter = pick(req.query, ['name']);
const options = pick(req.query, ['sortBy', 'limit', 'page']);
const plugins = await pluginService.queryPlugins(filter, options);
res.send(plugins);
});
// Контроллер для обновления параметров плагинов
export const setPluginConfig = catchAsync(async (req: RSetPlugin, res: Response) => {
const { name, config, enabled } = req.body;
try {
const updatedConfig = await pluginService.updatePluginConfig(name, enabled, config);
if (enabled == false) await terminateWorker(name);
else await restartWorker(name);
res.status(httpStatus.OK).json({
message: `Конфигурация плагина ${name} успешно обновлена`,
config: updatedConfig,
});
} catch (e: any) {
throw new ApiError(httpStatus.BAD_REQUEST, e.message);
}
});
// Контроллер для обновления параметров плагинов
export const getPluginSchema = catchAsync(async (req: RGetPluginSchema, res: Response) => {
const { name } = req.query;
try {
const schema = pluginService.getPluginSchema(name);
res.status(httpStatus.OK).json(schema);
} catch (e: any) {
throw new ApiError(httpStatus.BAD_REQUEST, e.message);
}
});
// Контроллер для обновления параметров плагинов
export const getPluginConfig = catchAsync(async (req: RGetPluginConfig, res: Response) => {
const { name } = req.query;
console.log('plugin', name);
const config = await pluginService.getPluginConfig(name);
res.status(httpStatus.OK).json(config);
});
@@ -0,0 +1,18 @@
import { settingService } from '../services';
import catchAsync from '../utils/catchAsync';
import httpStatus from 'http-status';
export const updateSettings = catchAsync(async (req, res) => {
const { settings } = req.body;
await settingService.updateSettings(settings);
res.status(httpStatus.OK).send();
});
export const getSettings = catchAsync(async (req, res) => {
const settings = await settingService.getSettings();
res.status(httpStatus.OK).send(settings);
});
@@ -0,0 +1,57 @@
import httpStatus from 'http-status';
import { getMonoStatus } from '../services/system.service';
import { getBlockchainInfo } from '../services/blockchain.service';
import { IHealthResponse, IInstall, RInstall } from '../types';
import { Request, Response } from 'express';
import catchAsync from '../utils/catchAsync';
import { systemService } from '../services';
import type { IInit, ISetVars, RInit, RSetVars, RSetWif } from '../types/auto-generated/system.validation';
import logger from '../config/logger';
export const init = catchAsync(async (req: RInit, res: Response) => {
const { body } = req;
await systemService.init(body as IInit);
res.status(httpStatus.OK).send();
});
export const install = catchAsync(async (req: RInstall, res: Response) => {
const { body } = req;
await systemService.install(body as IInstall);
logger.info('System installed');
res.status(httpStatus.OK).send();
});
export const setWif = catchAsync(async (req: RSetWif, res: Response) => {
await systemService.setWif(req.body);
res.status(httpStatus.OK).send();
});
export const getHealth = catchAsync(async (req: Request, res: Response) => {
const status = await getMonoStatus();
const blockchain = await getBlockchainInfo();
const result: IHealthResponse = {
status,
blockchain,
};
res.status(httpStatus.OK).send(result);
});
export const setVars = catchAsync(async (req: RSetVars, res: Response) => {
await systemService.setVars(req.body as ISetVars);
res.status(httpStatus.OK).send();
});
export const getVarsSchema = catchAsync(async (req: RSetVars, res: Response) => {
const schema = await systemService.getVarsSchema();
res.status(httpStatus.OK).send(schema);
});
export const getVars = catchAsync(async (req: RSetVars, res: Response) => {
const vars = await systemService.getVars();
res.status(httpStatus.OK).send(vars);
});
@@ -1,11 +1,30 @@
import http, { CREATED } from 'http-status';
import http from 'http-status';
import ApiError from '../utils/ApiError';
import catchAsync from '../utils/catchAsync';
import { userService, blockchainService, tokenService, orderService } from '../services';
import { IJoinCooperative, RCreateUser, RJoinCooperative } from '../types';
import { userService, tokenService, emailService, blockchainService } from '../services';
import { IAddUser, ICreateUser, RCreateUser } from '../types';
import httpStatus from 'http-status';
import pick from '../utils/pick';
import { IGetResponse } from '../types/common';
// import { IGetResponse } from '../types/common';
import { IUser, userStatus } from '../types';
import { Request, Response } from 'express';
import { generateUsername } from '../../tests/utils/generateUsername';
import config from '../config/config';
import logger from '../config/logger';
// import { Body, Controller, Get, Path, Post, Query, Route, SuccessResponse } from 'tsoa';
// @Route('users')
// export default class UsersController extends Controller {
// @SuccessResponse(201, 'Created') // Указываем статус 201 и описание
// @Post()
// public async createUser(@Body() req: RCreateUser): Promise<{ user: IUser; tokens: any }> {
// const user = await userService.createUser(req.body);
// const tokens = await tokenService.generateAuthTokens(user);
// this.setStatus(201); // Устанавливаем статус 201 Created
// return { user, tokens }; // Возвращаем объект в ответ
// }
// }
/**
* Порядок регистрации:
@@ -18,10 +37,11 @@ import { IGetResponse } from '../types/common';
* 7. (joinCooperative) Пользователь подписывает заявление на вступление и вызывает метод joinCooperative, прикладывая подписанное заявление.
* 8. Система сохраняет подписанное заявление
* 9. (createInitialPayment) Пользователь получает ордер на оплату.
* 9. Пользователь оплачивает ордер.
* 10. Система принимает оплату и регистрирует аккаунт в блокчейне.
* 11. (trx confirmpay) Пользователь подписывает велком-письмо с подтверждением собственноручности оплаты (транзакция в блокчейне для активации)
* 12. Регистрация продолжается ожиданием решения совета
*/
export const createUser = catchAsync(async (req: RCreateUser, res) => {
const user = await userService.createUser(req.body);
const tokens = await tokenService.generateAuthTokens(user);
@@ -29,55 +49,67 @@ export const createUser = catchAsync(async (req: RCreateUser, res) => {
res.status(http.CREATED).send({ user, tokens });
});
export const joinCooperative = catchAsync(async (req: RJoinCooperative, res) => {
await userService.joinCooperative(req.body);
export const addUser = catchAsync(async (req: Request, res: Response) => {
const body: IAddUser = req.body;
console.log('body: ', body);
const newUser: ICreateUser = {
...body,
public_key: '',
role: 'user',
username: generateUsername(),
};
res.status(httpStatus.OK).send();
const user = await userService.createUser(newUser);
user.status = userStatus['4_Registered'];
user.is_registered = true;
user.has_account = true;
await user.save();
try {
await blockchainService.addUser({
...body,
...newUser,
registrator: config.coopname,
referer: body.referer ? body.referer : '',
coopname: config.coopname,
meta: '',
});
const token = await tokenService.generateInviteToken(user.email);
await emailService.sendInviteEmail(req.body.email, token);
} catch (e: any) {
logger.warn('error on add user: ', e);
await userService.deleteUserByUsername(newUser.username);
throw new ApiError(httpStatus.BAD_GATEWAY, e.message);
}
res.status(httpStatus.CREATED).send({ user });
});
export const getUsers = catchAsync(async (req, res) => {
const filter = pick(req.query, ['username', 'role']);
const options = pick(req.query, ['sortBy', 'limit', 'page']);
filter.role = 'user';
const users = await userService.queryUsers(filter, options);
const result = {} as IGetResponse; //
//TODO wrong format answer
const data = [] as any;
for await (let user of users.results) {
const json = user.toJSON();
json.private_data = (await user.getPrivateData()) || {};
data.push(json);
}
result.results = data;
result.limit = users.limit;
result.page = users.page;
res.send(result);
res.send(users);
});
export const getUser = catchAsync(async (req, res) => {
const user = await userService.getUserByUsername(req.params.username);
if (!user) {
throw new ApiError(http.NOT_FOUND, 'Пользователь не найден');
}
const json = user.toJSON();
json.private_data = await user.getPrivateData();
res.send(json);
res.send(user);
});
export const updateUser = catchAsync(async (req, res) => {
const user = await userService.updateUserById(req.params.username, req.body);
if (req.user.role !== 'member' && req.user.role !== 'chairman')
throw new ApiError(httpStatus.FORBIDDEN, 'Только председатель или член совета может обновить данные пользователя');
const user = await userService.updateUserByUsername(req.params.username, req.body);
res.send(user);
});
export const deleteUser = catchAsync(async (req, res) => {
await userService.deleteUserById(req.params.username);
await userService.deleteUserByUsername(req.params.username);
res.status(http.NO_CONTENT).send();
});
@@ -2,6 +2,8 @@ import { io, Socket } from 'socket.io-client';
import { IAction } from '../types/common';
import { SovietContract } from 'cooptypes';
import { wsService } from '../services';
import logger from '../config/logger';
import config from '../config/config';
let clientSocket: Socket | undefined;
@@ -10,7 +12,7 @@ export const initSocketConnection = (serverUrl: string): void => {
clientSocket = io(serverUrl);
clientSocket.on('connect', () => {
console.log('Успешное подключение к серверу оповещений.');
logger.info('Success connection to notification server', { source: 'initSocketConnection' });
});
clientSocket.on('event', (event: IAction) => {
@@ -18,7 +20,7 @@ export const initSocketConnection = (serverUrl: string): void => {
});
clientSocket.on('connect_error', (error: Error) => {
console.error('Ошибка подключения к серверу оповещений:');
logger.error('Fail connect to notification server', { source: 'initSocketConnection' });
});
};
@@ -26,13 +28,19 @@ export const initSocketConnection = (serverUrl: string): void => {
export const closeSocketConnection = (): void => {
if (clientSocket) {
clientSocket.disconnect();
console.log('Client disconnected from server');
logger.info('Client disconnected from server', { source: 'closeSocketConnection' });
clientSocket = undefined;
}
};
async function processEvent(event: IAction) {
if (event.receiver === SovietContract.contractName.production && event.name === 'updateboard') {
wsService.updateBoard(event.data);
if (event.receiver === config.coopname) {
if (event.name === 'updateboard') {
wsService.updateBoard(event.data);
}
if (event.name === 'createboard') {
wsService.updateBoard(event.data);
}
}
}
+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 для хранения персональных данных пользователей
@@ -0,0 +1,104 @@
import { spawn, Thread, Worker } from 'threads';
import { PluginConfig } from '../models/pluginConfig.model';
// Объект для хранения запущенных воркеров
const workerMap: { [key: string]: { worker: any; thread: Worker } } = {};
export const initializeDefaultPlugins = async () => {
const defaultPlugins = [
{
name: 'powerup',
enabled: true,
config: {
dailyPackageSize: 5, // Размер ежедневного пакета в AXON
topUpAmount: 5, // Сумма пополнения при достижении порога (в AXON)
systemSymbol: 'AXON',
systemPrecision: 4,
thresholds: {
cpu: 5000, // Порог CPU в микросекундах
net: 1024, // Порог NET в байтах
ram: 10240, // Порог RAM в байтах
},
},
},
];
for (const plugin of defaultPlugins) {
const existingPlugin = await PluginConfig.findOne({ name: plugin.name });
if (!existingPlugin) {
await PluginConfig.create(plugin);
console.log(`Дефолтный плагин ${plugin.name} добавлен в базу.`);
}
}
};
export const pluginFactory = async () => {
const plugins = await PluginConfig.find({ enabled: true });
for (const pluginData of plugins) {
await initializeWorkerByName(pluginData.name);
}
};
// Функция для завершения воркера по имени плагина
export const terminateWorker = async (pluginName: string) => {
const workerData = workerMap[pluginName];
if (workerData) {
await workerData.thread.terminate();
delete workerMap[pluginName]; // Удаляем воркер из карты
console.log(`Воркер для плагина ${pluginName} завершен.`);
} else {
console.log(`Воркер для плагина ${pluginName} не найден.`);
}
};
// Функция для инициализации воркера по имени плагина
export const initializeWorkerByName = async (pluginName: string) => {
// Проверяем, запущен ли уже воркер
if (workerMap[pluginName]) {
console.log(`Воркер для плагина ${pluginName} уже запущен.`);
return;
}
// Получаем данные плагина из базы
const pluginData = await PluginConfig.findOne({ name: pluginName });
if (!pluginData || !pluginData.enabled) {
console.log(`Плагин ${pluginName} не найден или отключен.`);
return;
}
// Запускаем новый воркер
const thread = new Worker('../workers/plugin.worker.ts');
const worker = spawn(thread, { timeout: 30000 }); // Не используем await, чтобы не блокировать основной поток
//таймаут выше для локальных машин
// Сохраняем воркера и поток в объекте по имени плагина
workerMap[pluginName] = { worker, thread };
// Воркеры работают в фоне
worker.then(async (workerInstance) => {
try {
const result = await workerInstance.initializePlugin(pluginName);
console.log(result); // Плагин инициализирован успешно
} catch (err) {
console.error(err); // Обработка ошибки
}
});
console.log(`Воркер для плагина ${pluginName} успешно запущен.`);
};
// Функция для перезагрузки воркера по имени плагина
export const restartWorker = async (pluginName: string) => {
const workerData = workerMap[pluginName];
if (workerData) {
// Если воркер уже запущен, сначала завершаем его
console.log(`Перезагрузка воркера для плагина ${pluginName}...`);
await terminateWorker(pluginName);
}
// Затем запускаем воркер заново
await initializeWorkerByName(pluginName);
console.log(`Воркер для плагина ${pluginName} перезагружен.`);
};
+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 -11
View File
@@ -1,30 +1,42 @@
export * from './types';
import mongoose from 'mongoose';
import app from './app';
import config from './config/config';
import logger from './config/logger';
import { updateAuth } from './services/auth.service';
import { connectGenerator } from './services/data.service';
import { connectGenerator, diconnectGenerator } from './services/document.service';
import { initSocketConnection } from './controllers/ws.controller';
import { initializeDefaultPlugins, pluginFactory } from './factories/pluginFactory';
import { paymentService } from './services';
const SERVER_URL: string = process.env.SOCKET_SERVER || 'http://localhost:2222';
let server: any;
mongoose.connect(config.mongoose.url, config.mongoose.options).then(async () => {
mongoose.connect(config.mongoose.url).then(async () => {
logger.info('Connected to MongoDB');
//подключаемся к хранилищу приватных данных
// подключаемся к хранилищу приватных данных
await connectGenerator();
//подключаемся к ws-серверу
initSocketConnection(SERVER_URL);
// подключаемся к ws-серверу
await initSocketConnection(SERVER_URL);
// Запуск фабрики платежей
await paymentService.init();
// Инициализация дефолтных плагинов
await initializeDefaultPlugins();
// // Запуск плагинов
await pluginFactory();
server = app.listen(config.port, () => {
logger.info(`Listening to port ${config.port}`);
});
});
const exitHandler = () => {
const exitHandler = async () => {
if (server) {
server.close(() => {
logger.info('Server closed');
@@ -35,16 +47,20 @@ const exitHandler = () => {
}
};
const unexpectedErrorHandler = (error: any) => {
logger.error(error);
exitHandler();
const unexpectedErrorHandler = async (error: any) => {
console.log(error);
await diconnectGenerator();
await mongoose.disconnect();
logger.error(error, { source: 'unexpectedErrorHandler' });
await exitHandler();
};
process.on('uncaughtException', unexpectedErrorHandler);
process.on('unhandledRejection', unexpectedErrorHandler);
process.on('SIGTERM', () => {
process.on('SIGTERM', async () => {
logger.info('SIGTERM received');
await mongoose.disconnect();
if (server) {
server.close();
}
+19 -8
View File
@@ -6,20 +6,31 @@ import { roleRights } from '../config/roles';
const { UNAUTHORIZED, FORBIDDEN } = httpStatus;
const verifyCallback = (req, resolve, reject, requiredRights) => async (err, user, info) => {
const serverSecret = process.env.SERVER_SECRET;
// Check for server-secret header
if (serverSecret && req.headers['server-secret'] === serverSecret) {
return resolve();
}
if (err || info || !user) {
return reject(new ApiError(UNAUTHORIZED, 'Please authenticate'));
}
req.user = user;
if (requiredRights.length) {
const userRights = roleRights.get(user.role);
const hasRequiredRights = requiredRights.every((requiredRight) => userRights?.includes(requiredRight));
const userRights = roleRights.get(user.role) || [];
// if (process.env.IS_PRODUCTION) {
if (!hasRequiredRights && req.params.username !== user.username) {
return reject(new ApiError(FORBIDDEN, 'Недостаточно прав доступа'));
}
// }
// Determine if user has the required rights
const hasRequiredRights = requiredRights.every((requiredRight) => userRights.includes(requiredRight));
// Check if user is accessing their own resource
const isOwnResource =
(req.params.username && req.params.username === user.username) ||
(req.body.username && req.body.username === user.username);
if (!isOwnResource && (!hasRequiredRights || !requiredRights.length)) {
return reject(new ApiError(FORBIDDEN, 'Недостаточно прав доступа'));
}
resolve();
+6 -9
View File
@@ -10,12 +10,12 @@ export const errorConverter = (err, req, res, next) => {
if (!(error instanceof ApiError)) {
if (error instanceof RpcError) {
const message = error.json.error.details[0].message.replace('assertion failure with message: ', ''); // Получаем первую строку ошибки
const statusCode = error.json.code
const subCode = error.json.error.code
const statusCode = error.json.code;
const subCode = error.json.error.code;
error = new ApiError(statusCode, message, true, undefined, subCode);
} else {
const statusCode = error.statusCode || (error instanceof mongoose.Error ? httpStatus.BAD_REQUEST : httpStatus.INTERNAL_SERVER_ERROR);
const statusCode =
error.statusCode || (error instanceof mongoose.Error ? httpStatus.BAD_REQUEST : httpStatus.INTERNAL_SERVER_ERROR);
const message = error.message || httpStatus[statusCode];
error = new ApiError(statusCode, message, false, err.stack);
}
@@ -24,8 +24,7 @@ export const errorConverter = (err, req, res, next) => {
};
// eslint-disable-next-line no-unused-vars
export const errorHandler = (err, req, res, next) => {
export const errorHandler = (err, req, res, next?) => {
let { statusCode, message } = err;
if (config.env === 'production' && !err.isOperational) {
statusCode = httpStatus.INTERNAL_SERVER_ERROR;
@@ -41,9 +40,7 @@ export const errorHandler = (err, req, res, next) => {
...(config.env === 'development' && { stack: err.stack }),
};
if (config.env === 'development') {
logger.error(err);
}
logger.error(err);
res.status(statusCode).send(response);
};
@@ -5,7 +5,7 @@ import ApiError from '../utils/ApiError';
const validate = (schema) => (req, res, next) => {
// Фильтрация ключей, чтобы убедиться, что они существуют и не пусты
const validKeys = ['params', 'query', 'body'].filter(key => req[key] && Object.keys(req[key]).length > 0);
const validKeys = ['params', 'query', 'body'].filter((key) => req[key] && Object.keys(req[key]).length > 0);
const object = pick(req, validKeys);
const { value, error } = joi
@@ -21,5 +21,4 @@ const validate = (schema) => (req, res, next) => {
return next();
};
export default validate;
+2
View File
@@ -1,3 +1,5 @@
export { default as Token } from './token.model';
export { default as User } from './user.model';
export { default as Order } from './order.model';
export { default as Mono } from './mono.model';
export * from './settings.model';
@@ -0,0 +1,34 @@
import mongoose, { Schema, Document, model } from 'mongoose';
import { toJSON, paginate } from './plugins/index';
export interface IIpn {
provider: string;
data: object;
}
const ipnSchema = new Schema<IIpn>(
{
provider: {
type: String,
required: true,
},
data: {
type: Object,
required: true,
},
},
{
timestamps: true,
}
);
// add plugin that converts mongoose to json
ipnSchema.plugin(toJSON);
ipnSchema.plugin(paginate);
/**
* @typedef Ipn
*/
const IPN = model<IIpn>('Ipn', ipnSchema);
export default IPN;
@@ -0,0 +1,26 @@
import mongoose, { Schema } from 'mongoose';
import { paginate, toJSON } from './plugins';
import { IHealthStatus } from '../types';
export interface IMono {
coopname: string;
status: IHealthStatus;
}
const Status = {
Install: 'install',
Active: 'active',
Maintenance: 'maintenance',
};
const MonoSchema = new Schema<IMono>({
coopname: { type: String, required: true },
status: { type: String, required: true, enum: Object.values(Status) },
});
MonoSchema.plugin(toJSON);
MonoSchema.plugin(paginate);
const MonoModel = mongoose.model<IMono>('Mono', MonoSchema);
export default MonoModel;
+91 -56
View File
@@ -1,34 +1,50 @@
import mongoose, { Schema, Document, model } from 'mongoose';
import mongoose, { Schema, Document, model, type Model } from 'mongoose';
import { toJSON, paginate } from './plugins/index';
import AutoIncrementFactory from 'mongoose-sequence';
import { generateOrderSecret } from '../services/order.service';
import { type IOrder, orderStatus } from '../types/order.types';
interface IOrder extends Document {
creator: string;
type: string;
data: {
username: string
provider: string
quantity: string
};
order_id?: number;
payed?: boolean;
delivered?: boolean;
error?: object;
expired_at?: Date;
const AutoIncrement = AutoIncrementFactory(mongoose);
interface IOrderModel extends Model<IOrder> {
isEmailTaken(email: string, excludeUsername?: mongoose.Types.ObjectId): Promise<boolean>;
paginate(filter, options): any;
}
const orderSchema = new Schema<IOrder>({
creator: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
data: {
const orderSchema = new Schema<IOrder, IOrderModel>(
{
order_num: {
type: Number,
required: false,
unique: true,
},
creator: {
type: String,
required: true,
},
secret: {
type: String,
required: true,
private: true,
default: () => generateOrderSecret(16), // Генерация секрета по умолчанию
},
status: {
type: String,
enum: Object.values(orderStatus),
default: orderStatus.pending,
},
message: {
type: String,
required: false,
},
type: {
type: String,
enum: ['registration', 'deposit'],
required: true,
},
provider: {
type: String,
required: true
required: true,
},
username: {
type: String,
@@ -36,47 +52,66 @@ const orderSchema = new Schema<IOrder>({
},
quantity: {
type: String,
required: true
}
},
order_id: {
type: Number,
required: false,
},
payed: {
type: Boolean,
required: false,
default: false,
},
delivered: {
type: Boolean,
required: false,
default: false,
},
error: {
type: Object,
required: false,
},
expired_at: {
type: Date,
default: () => {
const now = new Date();
now.setDate(now.getDate() + 1); // Увеличиваем на одни сутки
return now;
required: true,
},
symbol: {
type: String,
required: true,
},
details: {
type: {
data: { type: Schema.Types.Mixed, required: true },
amount_plus_fee: { type: String, required: true },
amount_without_fee: { type: String, required: true },
fee_amount: { type: String, required: true },
fee_percent: { type: Number, required: true },
fact_fee_percent: { type: Number, required: true },
tolerance_percent: { type: Number, required: true },
},
required: false, // Поле может быть не обязательным
},
expired_at: {
type: Date,
default: () => {
const now = new Date();
now.setDate(now.getDate() + 1); // Увеличиваем на одни сутки
return now;
},
},
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }, // Связь с пользователем
},
},
{
timestamps: true,
{
timestamps: true,
}
);
// Хук после find для автоматического добавления private_data
orderSchema.post('find', async function (docs) {
for (const doc of docs) {
// Наполняем пользователя с помощью populate
await doc.populate('user');
// Если пользователь найден, вызываем асинхронный метод getPrivateData
if (doc.user) {
doc.user.private_data = await doc.user.getPrivateData();
}
// Проверяем поле expired_at и обновляем статус
if (doc.status != orderStatus.expired && doc.expired_at && new Date() > doc.expired_at) {
doc.status = orderStatus.expired;
await doc.save(); // Сохраняем изменения в документе
}
}
});
// add plugin that converts mongoose to json
orderSchema.plugin(toJSON);
orderSchema.plugin(paginate);
// Добавляем автоинкремент для поля order_num
orderSchema.plugin(AutoIncrement, { inc_field: 'order_num' });
/**
* @typedef Order
*/
const Order = model<IOrder>('Order', orderSchema);
const Order = model<IOrder, IOrderModel>('Order', orderSchema);
export default Order;
@@ -0,0 +1,17 @@
// models/PaymentState.ts
import { Schema, model } from 'mongoose';
interface IPaymentState {
accountNumber: string;
statementDate: string;
lastProcessedPage: number;
}
const PaymentStateSchema = new Schema<IPaymentState>({
accountNumber: { type: String, required: true },
statementDate: { type: String, required: true },
lastProcessedPage: { type: Number, required: true },
});
export const PaymentState = model<IPaymentState>('PaymentState', PaymentStateSchema);
@@ -0,0 +1,25 @@
import { Schema, model, type Model } from 'mongoose';
import type { IPluginSchema } from '../types/plugin.types';
import { paginate, toJSON } from './plugins';
interface IPaginate extends Model<IPluginSchema<any>> {
paginate(filter, options): any;
}
export const pluginSchema = new Schema<IPluginSchema<any>, IPaginate>(
{
name: { type: String, required: true },
enabled: { type: Boolean, default: true },
config: { type: Schema.Types.Mixed }, // Своя конфигурация для каждого плагина
},
{
timestamps: true,
}
);
// add plugin that converts mongoose to json
pluginSchema.plugin(toJSON);
pluginSchema.plugin(paginate);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const PluginConfig = model<IPluginSchema<any>, IPaginate>('Plugins', pluginSchema);
@@ -0,0 +1,24 @@
import { Schema, model, type Model } from 'mongoose';
import type { IPluginLog } from '../types/plugin.types';
import { paginate, toJSON } from './plugins';
interface IPaginate extends Model<IPluginLog<any>> {
paginate(filter, options): any;
}
export const pluginLog = new Schema<IPluginLog<any>, IPaginate>(
{
name: { type: String, required: true },
log: { type: Schema.Types.Mixed }, // Своя конфигурация для каждого плагина
},
{
timestamps: true,
}
);
// add plugin that converts mongoose to json
pluginLog.plugin(toJSON);
pluginLog.plugin(paginate);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const PluginLog = model<IPluginLog<any>, IPaginate>('PluginLogs', pluginLog);
@@ -1,5 +1,7 @@
/* eslint-disable no-param-reassign */
import mongoose from 'mongoose';
const paginate = (schema) => {
/**
* @typedef {Object} QueryResult
@@ -20,6 +22,11 @@ const paginate = (schema) => {
* @returns {Promise<QueryResult>}
*/
schema.statics.paginate = async function (filter, options) {
if (filter.id) {
filter._id = new mongoose.Types.ObjectId(String(filter.id));
delete filter.id;
}
let sort = '';
if (options.sortBy) {
const sortingCriteria: (string | never)[] = []; // Update the type of sortingCriteria array
@@ -29,7 +36,7 @@ const paginate = (schema) => {
});
sort = sortingCriteria.join(' ');
} else {
sort = 'createdAt';
sort = '-createdAt';
}
const limit = options.limit && parseInt(options.limit, 10) > 0 ? parseInt(options.limit, 10) : 10;
@@ -28,11 +28,11 @@ const toJSON = (schema) => {
}
});
// ret.id = ret._id.toString();
ret.id = ret._id.toString();
delete ret._id;
delete ret.__v;
delete ret.createdAt;
delete ret.updatedAt;
// delete ret.createdAt;
// delete ret.updatedAt;
if (transform) {
return transform(doc, ret, options);
}
@@ -0,0 +1,57 @@
import mongoose, { Schema, Document, model, type Model } from 'mongoose';
import { toJSON, paginate } from './plugins/index';
import _ from 'lodash';
import type { ISettings } from '../types';
export interface ISettingsModel extends Model<ISettings> {
updateSettings(newSettings: Partial<ISettings>): Promise<ISettings>;
getSettings(): Promise<ISettings>;
}
// Определение схемы
const settingsSchema = new Schema<ISettings>(
{
provider: {
name: { type: String, required: true, default: 'sberbank' },
client: { type: String, default: '' },
secret: { type: String, default: '' },
},
app: {
title: { type: String, required: true, default: 'Цифровой Кооператив' },
description: { type: String, required: true, default: 'Система электронного документооборота для кооперативов' },
},
},
{
timestamps: true,
}
);
// Подключаем плагины
settingsSchema.plugin(toJSON);
settingsSchema.plugin(paginate);
// Статические методы
settingsSchema.statics.updateSettings = async function (newSettings: Partial<ISettings>): Promise<ISettings> {
let settings = await this.findOne();
if (!settings) {
settings = new this();
}
// Универсальное обновление всех полей
_.merge(settings, newSettings);
return settings.save();
};
settingsSchema.statics.getSettings = async function (): Promise<ISettings> {
let settings = await this.findOne();
if (!settings) {
settings = new this();
await settings.save();
}
return settings;
};
// Модель
const Settings = model<ISettings, ISettingsModel>('Settings', settingsSchema);
export default Settings;
@@ -0,0 +1,51 @@
import mongoose, { Schema } from 'mongoose';
import { paginate, toJSON } from './plugins';
export enum tempdocType {
JoinStatement = 'joinStatement',
WalletAgreement = 'walletAgreement',
SignatureAgreement = 'signatureAgreement',
PrivacyAgreement = 'privacyAgreement',
UserAgreement = 'userAgreement',
}
export interface ITempDocument {
username: string;
type: tempdocType;
document: {
hash: string;
meta: object;
public_key: string;
signature: string;
};
}
const MonoSchema = new Schema<ITempDocument>({
username: { type: String, required: true },
type: { type: String, required: true, enum: ['joinStatement', 'walletAgreement'] },
document: {
public_key: {
type: String,
default: '',
},
signature: {
type: String,
default: '',
},
meta: {
type: Object,
default: {},
},
hash: {
type: String,
default: '',
},
},
});
MonoSchema.plugin(toJSON);
MonoSchema.plugin(paginate);
const TempDocument = mongoose.model<ITempDocument>('TempDocument', MonoSchema);
export default TempDocument;
@@ -25,7 +25,7 @@ const tokenSchema = new Schema<IToken>(
},
type: {
type: String,
enum: [tokenTypes.REFRESH, tokenTypes.RESET_PASSWORD, tokenTypes.VERIFY_EMAIL],
enum: [tokenTypes.REFRESH, tokenTypes.RESET_PASSWORD, tokenTypes.VERIFY_EMAIL, tokenTypes.INVITE],
required: true,
},
expires: {
@@ -39,6 +39,7 @@ const tokenSchema = new Schema<IToken>(
},
{
timestamps: true,
toJSON: { virtuals: true },
}
);
+44 -80
View File
@@ -1,42 +1,12 @@
import mongoose, { Document, Schema, model, Model } from 'mongoose';
import mongoose, { Schema, model, Model } from 'mongoose';
import validator from 'validator/index';
import bcryptjs from 'bcryptjs';
import { toJSON, paginate } from './plugins/index';
import { roles } from '../config/roles';
import { generator } from '../services/data.service';
import { generator } from '../services/document.service';
import { Cooperative } from 'cooptypes';
import { userStatus, type IUser } from '../types/user.types';
const { isEmail } = validator;
const { compare, hash } = bcryptjs;
export interface IUser extends Document {
username: string;
status: 'created' | 'joined' | 'payed' | 'registered' | 'active' | 'failed' | 'blocked';
message: string;
is_registered: boolean;
type: 'individual' | 'entrepreneur' | 'organization';
public_key: string;
referer: string;
email: string;
password: string;
role: string;
is_email_verified: boolean;
statement: {
hash: string;
meta: any;
public_key: string;
signature: string;
};
private_data:
| Cooperative.Users.IIndividualData
| Cooperative.Users.IEntrepreneurData
| Cooperative.Users.IOrganizationData
| null;
getPrivateData(): Promise<
Cooperative.Users.IIndividualData | Cooperative.Users.IEntrepreneurData | Cooperative.Users.IOrganizationData | null
>;
isPasswordMatch(password: string): Promise<boolean>;
}
interface IUserModel extends Model<IUser> {
isEmailTaken(email: string, excludeUsername?: mongoose.Types.ObjectId): Promise<boolean>;
@@ -53,8 +23,8 @@ const userSchema = new Schema<IUser, IUserModel>(
},
status: {
type: String,
enum: ['created', 'joined', 'payed', 'registered', 'active', 'failed', 'blocked'],
default: 'created',
enum: Object.values(userStatus),
default: userStatus['1_Created'],
},
message: {
type: String,
@@ -71,7 +41,7 @@ const userSchema = new Schema<IUser, IUserModel>(
},
public_key: {
type: String,
required: true,
default: '',
},
referer: {
type: String,
@@ -79,7 +49,7 @@ const userSchema = new Schema<IUser, IUserModel>(
},
email: {
type: String,
required: true,
required: false,
unique: true,
trim: true,
lowercase: true,
@@ -89,18 +59,6 @@ const userSchema = new Schema<IUser, IUserModel>(
}
},
},
password: {
type: String,
required: true,
trim: true,
minlength: 8,
validate(value) {
if (!value.match(/\d/) || !value.match(/[a-zA-Z]/)) {
throw new Error('Password must contain at least one letter and one number');
}
},
private: true, // used by the toJSON plugin
},
role: {
type: String,
enum: roles,
@@ -114,26 +72,13 @@ const userSchema = new Schema<IUser, IUserModel>(
type: Boolean,
default: false,
},
statement: {
public_key: {
type: String,
default: '',
},
signature: {
type: String,
default: '',
},
meta: {
type: Object,
default: {},
},
hash: {
type: String,
default: '',
},
initial_order: {
type: Schema.Types.ObjectId,
ref: 'Order',
},
},
{
minimize: false,
timestamps: true,
toJSON: { virtuals: true },
}
@@ -149,25 +94,44 @@ userSchema.statics.isEmailTaken = async function (email) {
return !!user;
};
// Хук после find для автоматического добавления private_data
userSchema.post('find', async function (docs) {
for (const doc of docs) {
// Наполняем пользователя с помощью populate
doc.private_data = await doc.getPrivateData();
}
});
// Хук после findOne для автоматического добавления private_data
userSchema.post('findOne', async function (doc) {
if (doc) {
doc.private_data = await doc.getPrivateData();
}
});
// Виртуальное свойство для private_data
userSchema
.virtual('private_data')
.get(function () {
return this._privateData; // Чтение из временного поля
})
.set(function (value) {
this._privateData = value; // Установка временного поля
});
// Асинхронный метод получения private_data
userSchema.methods.getPrivateData = async function (): Promise<
Cooperative.Users.IIndividualData | Cooperative.Users.IEntrepreneurData | Cooperative.Users.IOrganizationData | null
> {
return await generator.get(this.type, { username: this.username });
const privateData = (await generator.get(this.type, { username: this.username })) as
| Cooperative.Users.IIndividualData
| Cooperative.Users.IEntrepreneurData
| Cooperative.Users.IOrganizationData
| null;
this.private_data = privateData; // Устанавливаем виртуальное свойство
return privateData;
};
userSchema.methods.isPasswordMatch = async function (password) {
const user = this;
return compare(password, user.password);
};
userSchema.pre('save', async function (next) {
const user = this;
if (user.isModified('password')) {
user.password = await hash(user.password, 8);
}
next();
});
const User = model<IUser, IUserModel>('User', userSchema);
export default User;
@@ -0,0 +1,65 @@
import mongoose, { Schema, type Model } from 'mongoose';
import { paginate, toJSON } from './plugins';
import { decrypt, encrypt } from '../utils/aes';
export interface IVault {
username: string;
permission: wifPermissions;
wif: string;
}
export enum wifPermissions {
'Active' = 'active',
}
interface IVaultActions extends Model<IVault> {
getWif(username: string, permission?: wifPermissions): Promise<string | null>;
setWif(username: string, wif: string, permission?: wifPermissions): Promise<boolean>;
}
const VaultSchema = new Schema<IVault, IVaultActions>({
username: { type: String, required: true },
permission: { type: String, required: true, enum: Object.values(wifPermissions) },
wif: { type: String, required: true, private: true },
});
// Метод для извлечения WIF с дефолтным значением permission
VaultSchema.statics.getWif = async function (
username: string,
permission: wifPermissions = wifPermissions.Active
): Promise<string | null> {
const vault = await this.findOne({ username, permission });
if (vault) {
try {
return decrypt(vault.wif); // Расшифровываем WIF перед возвратом
} catch (error) {
console.error('Ошибка при расшифровке WIF:', error);
return null;
}
}
return null;
};
// Метод для обновления или создания WIF с дефолтным значением permission
VaultSchema.statics.setWif = async function (
username: string,
wif: string,
permission: wifPermissions = wifPermissions.Active
): Promise<boolean> {
const encryptedWif = encrypt(wif); // Шифруем WIF перед сохранением
const result = await this.findOneAndUpdate(
{ username, permission },
{ wif: encryptedWif },
{ new: true, upsert: true } // upsert создает новую запись, если не существует
);
return result !== null;
};
VaultSchema.plugin(toJSON);
VaultSchema.plugin(paginate);
const Vault = mongoose.model<IVault, IVaultActions>('Vault', VaultSchema);
export default Vault;
+5
View File
@@ -0,0 +1,5 @@
import * as powerup from './powerup/powerup.plugin';
export const PluginRegistry = {
powerup,
};
@@ -0,0 +1,20 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.1.3](https://github.com/coopenomics/monocoop/compare/v2.1.2-alpha.10...v2.1.3) (2024-10-26)
## [2.1.2](https://github.com/coopenomics/monocoop/compare/v2.1.1...v2.1.2) (2024-10-19)
**Note:** Version bump only for package powerup
## [2.1.2](https://github.com/coopenomics/monocoop/compare/v2.1.1...v2.1.2) (2024-10-19)
**Note:** Version bump only for package powerup
@@ -0,0 +1,14 @@
{
"name": "powerup",
"version": "2.1.3",
"description": "",
"private": true,
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"gitHead": "7b59ea4b96991ed06abdc92e6d7835632845e3a5"
}
@@ -0,0 +1,196 @@
import { IPlugin, type IPluginLog, type IPluginSchema } from '../../types/plugin.types';
import Joi from 'joi';
import cron from 'node-cron';
import { blockchainService } from '../../services';
import logger from '../../config/logger';
import { default as coopConfig } from '../../config/config';
import { PluginConfig } from '../../models/pluginConfig.model';
import { PluginLog } from '../../models/pluginLog.model';
import type { IGetPluginConfig } from '../../types';
import type { Document, Types } from 'mongoose';
// Интерфейс для параметров конфигурации плагина powerup
export interface IConfig {
dailyPackageSize: number; // Размер ежедневного пакета в AXON
topUpAmount: number; // Сумма пополнения при достижении порога (в AXON)
systemSymbol: string;
systemPrecision: number;
lastDailyReplenishmentDate: Date;
thresholds: {
cpu: number; // Порог CPU в микросекундах
net: number; // Порог NET в байтах
ram: number; // Порог RAM в байтах
};
}
export interface ILog {
type: 'daily' | 'now';
amount: string;
resources: {
username: string;
ram_usage: any;
ram_quota: any;
net_limit: any;
cpu_limit: any;
};
}
export class Plugin implements IPlugin {
name = 'powerup';
data!: IPluginSchema<IConfig>;
public pluginSchema = Joi.object<IConfig>({
dailyPackageSize: Joi.number().required(),
topUpAmount: Joi.number().required(),
thresholds: Joi.object({
cpu: Joi.number().required(),
net: Joi.number().required(),
ram: Joi.number().required(),
}).required(),
});
async initialize() {
const pluginData = await PluginConfig.findOne({ name: this.name });
if (!pluginData) throw new Error('Конфиг не найден');
this.data = pluginData;
logger.info(`Инициализация ${this.name} с конфигурацией`, this.data);
// Проверяем, было ли ежедневное пополнение в последние 24 часа
const lastDate = this.data.config.lastDailyReplenishmentDate;
const now = new Date();
if (lastDate) {
const diffInHours = Math.abs(now.getTime() - lastDate.getTime()) / 36e5; // Разница во времени в часах
if (diffInHours < 24) {
logger.info('Ежедневное пополнение уже выполнялось в последние 24 часа. Повторное пополнение не требуется.');
} else {
logger.info('Ежедневное пополнение не выполнялось в последние 24 часа. Выполняем пополнение...');
await this.runDailyTask();
}
} else {
logger.info('Дата последнего пополнения отсутствует. Выполняем пополнение...');
await this.runDailyTask();
}
// Регистрация cron-задачи для ежедневного пополнения
cron.schedule('0 0 * * *', () => {
logger.info('Запуск ежедневной задачи пополнения');
this.runDailyTask();
});
// Регистрация cron-задачи для проверки ресурсов каждую минуту
cron.schedule('* * * * *', () => {
logger.info('Запуск задачи проверки ресурсов');
this.runTask();
});
// eslint-disable-next-line @typescript-eslint/no-empty-function
await new Promise(() => {});
}
private getQuantity(amount: number): string {
return `${amount.toFixed(this.data.config.systemPrecision)} ${this.data.config.systemSymbol}`;
}
// Ежедневная задача пополнения
private async runDailyTask() {
const quantity = this.getQuantity(this.data.config.dailyPackageSize);
logger.info(`Выполнение ежедневного пополнения на сумму ${quantity}`);
try {
// Получаем имя пользователя из окружения или другой конфигурации
const username = coopConfig.coopname;
const account = await blockchainService.getBlockchainAccount(username);
await blockchainService.powerUp(username, quantity);
this.data.config.lastDailyReplenishmentDate = new Date();
this.data.markModified('config'); // Помечаем config как измененное поле
await this.data.save();
await this.log({
type: 'daily',
amount: quantity,
resources: {
username: account.account_name,
ram_usage: account.ram_usage,
ram_quota: account.ram_quota,
net_limit: account.net_limit,
cpu_limit: account.cpu_limit,
},
});
logger.info('Ежедневное пополнение выполнено успешно');
} catch (error) {
console.error('Ошибка при выполнении ежедневного пополнения:', error);
}
}
private async log(action: ILog) {
await PluginLog.create({ name: this.name, log: action });
}
// Задача проверки и пополнения ресурсов
private async runTask() {
logger.info(`Задача плагина ${this.name} выполняется...`);
try {
// Получаем имя пользователя из окружения или другой конфигурации
const username = coopConfig.coopname;
const account = await blockchainService.getBlockchainAccount(username);
// Получаем текущие значения квот
const cpuLimit = account.cpu_limit;
const netLimit = account.net_limit;
const ramQuota = account.ram_quota;
const ramUsage = account.ram_usage;
const availableRam = ramQuota - ramUsage;
// Проверяем пороги и пополняем при необходимости
let needPowerUp = false;
if (cpuLimit.available <= this.data.config.thresholds.cpu) {
logger.info(`CPU квота ниже порога (${cpuLimit.available} µs).`);
needPowerUp = true;
}
if (netLimit.available <= this.data.config.thresholds.net) {
logger.info(`NET квота ниже порога (${netLimit.available} bytes).`);
needPowerUp = true;
}
if (availableRam <= this.data.config.thresholds.ram) {
logger.info(`RAM квота ниже порога (${availableRam} bytes).`);
needPowerUp = true;
}
if (needPowerUp) {
// Выполняем пополнение ресурсов
const quantity = this.getQuantity(this.data.config.topUpAmount);
await blockchainService.powerUp(username, quantity);
logger.info(`Пополнение выполнено на сумму ${quantity}.`);
await this.log({
type: 'now',
amount: quantity,
resources: {
username: account.account_name,
ram_usage: account.ram_usage,
ram_quota: account.ram_quota,
net_limit: account.net_limit,
cpu_limit: account.cpu_limit,
},
});
} else {
logger.info('Квоты ресурсов в норме, пополнение не требуется.');
}
} catch (error) {
console.error('Ошибка при проверке и пополнении ресурсов:', error);
}
}
}
@@ -7,13 +7,13 @@ import auth from '../../middlewares/auth';
const router = Router();
router.post('/login', validate(authValidation.login), authController.login);
router.post('/logout', validate(authValidation.logout), authController.logout);
router.post('/refresh-tokens', validate(authValidation.refreshTokens), authController.refreshTokens);
router.post('/forgot-password', validate(authValidation.forgotPassword), authController.forgotPassword);
router.post('/reset-password', validate(authValidation.resetPassword), authController.resetPassword);
router.post('/send-verification-email', auth(), authController.sendVerificationEmail);
router.post('/verify-email', validate(authValidation.verifyEmail), authController.verifyEmail);
router.post('/login', validate(authValidation.RLogin), authController.login);
router.post('/logout', validate(authValidation.RLogout), authController.logout);
router.post('/refresh-tokens', validate(authValidation.RRefreshTokens), authController.refreshTokens);
router.post('/lost-key', validate(authValidation.RForgotKey), authController.lostKey);
router.post('/reset-key', validate(authValidation.RResetKey), authController.resetKey);
router.post('/send-verification-email', auth('sendVerificationEmail'), authController.sendVerificationEmail);
router.post('/verify-email', validate(authValidation.RVerifyEmail), authController.verifyEmail);
export default router;
@@ -21,55 +21,7 @@ export default router;
* @swagger
* tags:
* name: Auth
* description: Authentication
*/
/**
* @swagger
* /auth/register:
* post:
* summary: Register as user
* tags: [Auth]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - name
* - email
* - password
* properties:
* name:
* type: string
* email:
* type: string
* format: email
* description: must be unique
* password:
* type: string
* format: password
* minLength: 8
* description: At least one number and one letter
* example:
* name: fake name
* email: fake@example.com
* password: password1
* responses:
* "201":
* description: Created
* content:
* application/json:
* schema:
* type: object
* properties:
* user:
* $ref: '#/components/schemas/User'
* tokens:
* $ref: '#/components/schemas/AuthTokens'
* "400":
* $ref: '#/components/responses/DuplicateEmail'
* description: Управление аудентификацией
*/
/**
@@ -2,15 +2,14 @@ import { Router } from 'express';
import auth from '../../middlewares/auth';
import validate from '../../middlewares/validate';
import * as coopValidation from '../../validations/coop.validation';
// import { loadAgenda, loadStaff, } from '../../validations/coop.validation';
import * as coopController from '../../controllers/coop.controller';
const router = Router();
router.route('/agenda').get(auth('loadAgenda'), validate(coopValidation.loadAgenda), coopController.loadAgenda);
router.route('/staff').get(auth('loadStaff'), validate(coopValidation.loadStaff), coopController.loadStaff);
router.route('/contacts').get(coopController.loadContacts);
router.route('/info').get(auth('loadInfo'), coopController.loadInfo);
router.route('/contacts').get(coopController.loadContacts);
export default router;
@@ -1,32 +0,0 @@
import express from "express"
import auth from "../../middlewares/auth"
import validate from "../../middlewares/validate"
import * as dataValidation from "../../validations/data.validation"
import { dataController } from "../../controllers"
const router = express.Router()
router
.route("/generate")
.post(
auth(),
validate(dataValidation.RGenerate),
dataController.generateDocument
)
router
.route("/get-documents")
.get(
auth("getDocuments"),
validate(dataValidation.RGetDocuments),
dataController.getDocuments
)
router
.route("/get-my-documents")
.get(
auth(),
validate(dataValidation.RGetDocuments),
dataController.getDocuments
)
export default router
@@ -0,0 +1,21 @@
import express from 'express';
import auth from '../../middlewares/auth';
import validate from '../../middlewares/validate';
import * as documentValidation from '../../validations/document.validation';
import { documentController } from '../../controllers';
const router = express.Router();
router
.route('/generate')
.post(auth('regenerateDocument'), validate(documentValidation.RGenerate), documentController.generateDocument);
router
.route('/get-documents')
.get(auth('getDocuments'), validate(documentValidation.RGetDocuments), documentController.getDocuments);
// router
// .route('/get-my-documents')
// .get(auth('getMyDocuments'), validate(documentValidation.RGetDocuments), documentController.getDocuments);
export default router;
+22 -2
View File
@@ -4,14 +4,22 @@ import userRoute from './user.route';
import docsRoute from './docs.route';
import orderRoute from './order.route';
import coopRoute from './coop.route';
import dataRoute from './data.route';
import monoRoute from './system.route';
import dataRoute from './document.route';
import notifyRoute from './notify.route';
import participantsRoute from './participant.route';
import methodRoute from './method.route';
import pluginRoute from './plugin.route';
import config from '../../config/config';
const router = Router();
const defaultRoutes = [
{
path: '/system',
route: monoRoute,
},
{
path: '/auth',
route: authRoute,
@@ -21,9 +29,17 @@ const defaultRoutes = [
route: userRoute,
},
{
path: '/data',
path: '/participants',
route: participantsRoute,
},
{
path: '/documents',
route: dataRoute,
},
{
path: '/methods',
route: methodRoute,
},
{
path: '/orders',
route: orderRoute,
@@ -36,6 +52,10 @@ const defaultRoutes = [
path: '/notify',
route: notifyRoute,
},
{
path: '/plugins',
route: pluginRoute,
},
];
const devRoutes = [
@@ -0,0 +1,21 @@
import { Router } from 'express';
import { methodController } from '../../controllers';
import validate from '../../middlewares/validate';
import { methodValidation } from '../../validations';
import auth from '../../middlewares/auth';
const router = Router();
router
.route('/:username?')
.get(auth('manageMyMethods'), validate(methodValidation.RGetListPaymentMethods), methodController.listPaymentMethods);
router
.route('/:username/add')
.post(auth('manageMyMethods'), validate(methodValidation.RSavePaymentMethod), methodController.addPaymentMethod);
router
.route('/:username/delete')
.post(auth('manageMyMethods'), validate(methodValidation.RDeletePaymentMethod), methodController.deletePaymentMethod);
export default router;
@@ -1,9 +1,8 @@
import { Router } from 'express';
import { orderController } from '../../controllers';
import { methodController, orderController } from '../../controllers';
import validate from '../../middlewares/validate';
import * as orderValidation from '../../validations/order.validation';
import { orderValidation } from '../../validations';
import auth from '../../middlewares/auth';
import { Request, Response } from 'express';
const router = Router();
@@ -21,10 +20,10 @@ const router = Router();
* ICreatedPayment:
* type: object
* required:
* - order_id
* - order_num
* - confirmation_token
* properties:
* order_id:
* order_num:
* type: number
* confirmation_token:
* type: string
@@ -41,7 +40,7 @@ const router = Router();
* 200:
* description: Успешное выполнение запроса.
*/
router.route('/ipn').post(validate(orderValidation.RRecieveIPN), orderController.catchIPN);
router.route('/:provider/ipn').post(validate(orderValidation.RRecieveIPN), orderController.catchIPN);
/**
* @swagger
@@ -66,7 +65,9 @@ router.route('/ipn').post(validate(orderValidation.RRecieveIPN), orderController
* schema:
* $ref: '#/components/schemas/ICreatedPayment'
*/
router.route('/initial').post(auth(), validate(orderValidation.RCreateInitialPayment), orderController.createInitialPayment);
router
.route('/initial')
.post(auth('initialPayment'), validate(orderValidation.RCreateInitialPayment), orderController.createInitialPayment);
/**
* @swagger
@@ -91,6 +92,15 @@ router.route('/initial').post(auth(), validate(orderValidation.RCreateInitialPay
* schema:
* $ref: '#/components/schemas/ICreatedPayment'
*/
router.route('/deposit').post(auth(), validate(orderValidation.RCreateDeposit), orderController.createDeposit);
router
.route('/deposit')
.post(auth('createDeposit'), validate(orderValidation.RCreateDeposit), orderController.createDeposit);
router
.route('/set-order-status')
.post(auth('manageOrders'), validate(orderValidation.RSetOrderStatus), orderController.setStatus);
router.route('/all').get(auth('manageOrders'), validate(orderValidation.RGetCoopOrders), orderController.getOrders);
router.route('/:username').get(auth('getMyOrders'), validate(orderValidation.RGetMyOrders), orderController.getMyOrders);
export default router;
@@ -0,0 +1,13 @@
import { Router } from 'express';
import auth from '../../middlewares/auth';
import validate from '../../middlewares/validate';
import { participantValidation } from '../../validations';
import { participantController } from '../../controllers';
const router = Router();
router
.route('/join-cooperative')
.post(auth('joinCooperative'), validate(participantValidation.RJoinCooperative), participantController.joinCooperative);
export default router;
@@ -0,0 +1,20 @@
import { Router } from 'express';
import { pluginController } from '../../controllers';
import auth from '../../middlewares/auth';
import validate from '../../middlewares/validate';
import { pluginValidation } from '../../validations';
const router = Router();
router.route('/').post(auth('setPlugin'), validate(pluginValidation.RSetPlugin), pluginController.setPluginConfig);
router.route('/').get(auth('setPlugin'), validate(pluginValidation.RGetPluginList), pluginController.getPluginList);
router
.route('/schema')
.get(auth('setPlugin'), validate(pluginValidation.RGetPluginSchema), pluginController.getPluginSchema);
router
.route('/config')
.get(auth('setPlugin'), validate(pluginValidation.RGetPluginConfig), pluginController.getPluginConfig);
export default router;
@@ -0,0 +1,30 @@
import { Router } from 'express';
import { settingController, systemController } from '../../controllers';
import auth from '../../middlewares/auth';
import validate from '../../middlewares/validate';
import { systemValidation } from '../../validations';
const router = Router();
//init - устанавливает данные организации кооператива
router.route('/init').post(auth('init'), validate(systemValidation.RInit), systemController.init);
router.route('/install').post(validate(systemValidation.RInstall), systemController.install);
router.route('/wif').post(auth('setWif'), validate(systemValidation.RSetWif), systemController.setWif);
//wif
router.route('/vars/schema').get(auth('getVars'), systemController.getVarsSchema);
router.route('/vars').get(auth('getVars'), systemController.getVars);
router.route('/vars').post(auth('setVars'), validate(systemValidation.RSetVars), systemController.setVars);
router.route('/health').get(systemController.getHealth);
router.route('/settings').get(auth('manageSettings'), settingController.getSettings);
router
.route('/settings')
.post(auth('manageSettings'), validate(systemValidation.RUpdateSettings), settingController.updateSettings);
export default router;
@@ -11,13 +11,11 @@ router
.post(validate(userValidation.RCreateUser), userController.createUser)
.get(auth('getUsers'), validate(userValidation.RGetUsers), userController.getUsers);
router
.route('/join-cooperative')
.post(validate(userValidation.RJoinCooperative), userController.joinCooperative)
router.route('/add').post(auth('addUser'), validate(userValidation.RAddUser), userController.addUser);
router
.route('/:username')
.get(auth('getUsers'), validate(userValidation.RGetUser), userController.getUser)
.get(auth('getSelf'), validate(userValidation.RGetUser), userController.getUser)
.patch(auth('manageUsers'), validate(userValidation.RUpdateUser), userController.updateUser);
export default router;
@@ -200,3 +198,33 @@ export default router;
* $ref: '#/components/responses/NotFound'
*
*/
// /**
// * @swagger
// * /users/add:
// * post:
// * summary: Добавить пользователя без заявления
// * description: Только администраторы могут добавлять новых пользователей без прохождения процедуры подачи заявления. Это используется для добавления действующих пайщиков.
// * tags: [Users]
// * security:
// * - bearerAuth: []
// * requestBody:
// * required: true
// * content:
// * application/json:
// * schema:
// * "$ref": '#/components/schemas/IAddUser'
// * responses:
// * "201":
// * description: Пользователь успешно добавлен
// * content:
// * application/json:
// * schema:
// * $ref: '#/components/schemas/User'
// * "400":
// * $ref: '#/components/responses/BadRequest'
// * "401":
// * $ref: '#/components/responses/Unauthorized'
// * "403":
// * $ref: '#/components/responses/Forbidden'
// */
@@ -1,67 +1,50 @@
import httpStatus from 'http-status';
import * as tokenService from './token.service';
import userService from './user.service';
import { getSoviet } from './blockchain.service';
import * as userService from './user.service';
import Token from '../models/token.model';
import ApiError from '../utils/ApiError';
import { tokenTypes } from '../config/tokens';
import { IRefreshTokens } from '../types';
import { getUserByEmail } from './user.service';
import { Bytes, Checksum256, Signature } from '@wharfkit/antelope';
import { getBlockchainAccount, getBlockchainInfo, hasActiveKey } from '../services/blockchain.service';
import config from '../config/config';
import { blockchainService } from '.';
export const updateAuth = async () => {
try {
const board = await getSoviet(process.env.COOPNAME);
// TODO снимать права с тех, кто уже не в совете
// eslint-disable-next-line no-restricted-syntax
for (const member of board.members) {
// eslint-disable-next-line no-await-in-loop
const user = await userService.getUserByUsername(member.username);
if (member.position === 'chairman' && !user) {
// eslint-disable-next-line no-await-in-loop
// await userService.createUser({
// username: member.username,
// public_key: '-',
// email: process.env.CHAIRMAN_EMAIL ,
// password: process.env.CHAIRMAN_PASSWORD as string,
// is_registered: true,
// is_organization: false,
// user_profile: {
// first_name: 'Имя',
// last_name: 'Фамилия',
// middle_name: 'Отчество',
// birthday: '23-42-3423',
// phone: '+7902294404',
// },
// signature: '-',
// signature_hash: '-',
// role: 'chairman',
// });
} else if (member.position === 'chairman' && user) {
user.role = 'chairman';
user.password = process.env.CHAIRMAN_PASSWORD || 'password';
// eslint-disable-next-line no-await-in-loop
await user.save();
} else if (user) {
user.role = 'admin';
// eslint-disable-next-line no-await-in-loop
await user.save();
}
export const loginUserWithSignature = async (email, now, signature) => {
const user = await getUserByEmail(email);
if (!user) {
throw new ApiError(httpStatus.UNAUTHORIZED, 'Пользователь не найден');
}
const bytes = Bytes.fromString(now, 'utf8');
const checksum = Checksum256.hash(bytes);
const wharf_signature = Signature.from(signature);
const publicKey = wharf_signature.recoverDigest(checksum).toLegacyString();
const info = await getBlockchainInfo();
const blockchainDate = new Date(info.head_block_time).getTime();
const userData = new Date(now).getTime();
const differenceInSeconds = (blockchainDate - userData) / 1000;
if (differenceInSeconds > 30) {
throw new ApiError(httpStatus.BAD_REQUEST, 'Время подписи и время блокчейна превышает допустимое расхождение');
}
if (config.env !== 'test') {
try {
const blockchainAccount = await getBlockchainAccount(user.username);
const hasKey = hasActiveKey(blockchainAccount, publicKey);
if (!hasKey) throw new ApiError(httpStatus.UNAUTHORIZED, 'Неверный приватный ключ');
} catch (e) {
throw new ApiError(httpStatus.UNAUTHORIZED, 'Неверный приватный ключ');
}
} catch (e: any) {
// eslint-disable-next-line no-console
console.log('Ошибка при автоматической проверке целевой авторизации: ', e.message);
}
};
/**
* Login with username and password
* @param {string} username
* @param {string} password
* @returns {Promise<User>}
*/
export const loginUserWithUsernameAndPassword = async (username, password) => {
const user = await userService.getUserByUsername(username);
if (!user || !(await user.isPasswordMatch(password))) {
throw new ApiError(httpStatus.UNAUTHORIZED, 'Incorrect username or password');
}
return user;
};
@@ -75,7 +58,7 @@ export const logout = async (refreshToken) => {
if (!refreshTokenDoc) {
throw new ApiError(httpStatus.NOT_FOUND, 'Not found');
}
await refreshTokenDoc.remove();
await refreshTokenDoc.deleteOne();
};
/**
@@ -83,16 +66,14 @@ export const logout = async (refreshToken) => {
* @param {string} refreshToken
* @returns {Promise<Object>}
*/
export const refreshAuth = async (refreshToken) => {
export const refreshAuth = async (data: IRefreshTokens) => {
try {
const refreshTokenDoc = await tokenService.verifyToken(refreshToken, tokenTypes.REFRESH);
const user = await userService.getUserByUsername(refreshTokenDoc.user);
const refreshTokenDoc = await tokenService.verifyToken(data.refreshToken, tokenTypes.REFRESH);
const user = await userService.getUserById(refreshTokenDoc.user);
if (!user) {
throw new Error();
}
await refreshTokenDoc.remove();
await refreshTokenDoc.deleteOne();
return tokenService.generateAuthTokens(user);
} catch (error) {
throw new ApiError(httpStatus.UNAUTHORIZED, 'Please authenticate');
@@ -100,22 +81,34 @@ export const refreshAuth = async (refreshToken) => {
};
/**
* Reset password
* @param {string} resetPasswordToken
* @param {string} newPassword
* Reset key
* @param {string} resetKeyToken
* @param {string} publicKey
* @returns {Promise}
*/
export const resetPassword = async (resetPasswordToken, newPassword) => {
export const resetKey = async (resetKeyToken, publicKey) => {
try {
const resetPasswordTokenDoc = await tokenService.verifyToken(resetPasswordToken, tokenTypes.RESET_PASSWORD);
const user = await userService.getUserByUsername(resetPasswordTokenDoc.user);
const resetKeyTokenDoc = await tokenService.verifyToken(resetKeyToken, [tokenTypes.RESET_PASSWORD, tokenTypes.INVITE]);
const user = await userService.getUserById(resetKeyTokenDoc.user);
if (!user) {
throw new Error();
}
await userService.updateUserById(user.username, { password: newPassword });
await Token.deleteMany({ user: user.username, type: tokenTypes.RESET_PASSWORD });
if (config.env !== 'test')
await blockchainService.changeKey({
coopname: config.coopname,
changer: config.coopname,
username: user.username,
public_key: publicKey,
});
await userService.updateUserById(user._id, { public_key: publicKey });
await Token.deleteMany({ user: user._id, type: tokenTypes.RESET_PASSWORD });
} catch (error) {
throw new ApiError(httpStatus.UNAUTHORIZED, 'Password reset failed');
console.log(error);
throw new ApiError(httpStatus.UNAUTHORIZED, 'Token reset failed');
}
};
@@ -127,12 +120,12 @@ export const resetPassword = async (resetPasswordToken, newPassword) => {
export const verifyEmail = async (verifyEmailToken) => {
try {
const verifyEmailTokenDoc = await tokenService.verifyToken(verifyEmailToken, tokenTypes.VERIFY_EMAIL);
const user = await userService.getUserByUsername(verifyEmailTokenDoc.user);
const user = await userService.getUserById(verifyEmailTokenDoc.user);
if (!user) {
throw new Error();
}
await Token.deleteMany({ user: user.username, type: tokenTypes.VERIFY_EMAIL });
await userService.updateUserById(user.username, { is_email_verified: true });
await Token.deleteMany({ user: user._id, type: tokenTypes.VERIFY_EMAIL });
await userService.updateUserById(user._id, { is_email_verified: true });
} catch (error) {
throw new ApiError(httpStatus.UNAUTHORIZED, 'Email verification failed');
}
@@ -1,14 +1,55 @@
import { Api, JsonRpc } from 'eosjs';
import { JsSignatureProvider } from 'eosjs/dist/eosjs-jssig';
import { TextEncoder, TextDecoder } from 'util';
import fetch from 'isomorphic-fetch';
import EosApi from 'eosjs-api';
import getInternalAction from '../utils/getInternalAction';
import { GatewayContract, RegistratorContract, SovietContract } from 'cooptypes';
import { IUser } from '../models/user.model';
import { GatewayContract, RegistratorContract, SovietContract, type SystemContract } from 'cooptypes';
import { IUser } from '../types/user.types';
import { GetAccountResult, GetInfoResult } from 'eosjs/dist/eosjs-rpc-interfaces';
import config from '../config/config';
import TempDocument, { tempdocType } from '../models/tempDocument.model';
import ApiError from '../utils/ApiError';
import httpStatus from 'http-status';
import type { IOrder } from '../types/order.types';
import type { IBCAction } from '../types';
import Vault from '../models/vault.model';
const rpc = new JsonRpc(process.env.BLOCKCHAIN_RPC as string, { fetch });
function hasActiveKey(account, publicKey) {
const activePermissions = account.permissions.find((p: any) => p.perm_name === 'active');
if (!activePermissions) return false;
return activePermissions.required_auth.keys.some((key: any) => key.key === publicKey);
}
async function getBlockchainInfo(): Promise<GetInfoResult> {
const api = getApi();
return await api.getInfo({});
}
async function getBlockchainAccount(username): Promise<GetAccountResult> {
const api = getApi();
return await api.getAccount(username);
}
export async function transact(actions: any): Promise<any> {
const instance = await getInstance(config.coopname);
const result = await instance.transact(
{
actions,
},
{
blocksBehind: 3,
expireSeconds: 30,
}
);
return result;
}
/**
* Получить инстанс для осуществления транзакции в блокчейн.
* @param {username} - авторизация от аккаунта
@@ -16,15 +57,19 @@ const rpc = new JsonRpc(process.env.BLOCKCHAIN_RPC as string, { fetch });
* @returns {eosjs-api}
*/
async function getInstance(wif) {
async function getInstance(username: string) {
const wif = await Vault.getWif(username);
if (!wif) throw new ApiError(httpStatus.BAD_GATEWAY, 'Не найден приватный ключ для совершения операции');
const signatureProvider = new JsSignatureProvider([wif]);
const api = new Api({ rpc, signatureProvider, textDecoder: new TextDecoder(), textEncoder: new TextEncoder() });
const api = new Api({ rpc, signatureProvider });
return api;
}
async function getApi() {
function getApi() {
const options = {
httpEndpoint: process.env.BLOCKCHAIN_RPC, // default, null for cold-storage
httpEndpoint: process.env.BLOCKCHAIN_RPC, //
verbose: false, // API logging
fetchConfiguration: {},
};
@@ -63,9 +108,9 @@ async function getCooperative(coopname) {
const [cooperative] = await lazyFetch(
api,
process.env.REGISTRATOR_CONTRACT,
process.env.REGISTRATOR_CONTRACT,
'orgs',
RegistratorContract.contractName.production,
RegistratorContract.contractName.production,
RegistratorContract.Tables.Cooperatives.tableName,
coopname,
coopname,
1
@@ -73,9 +118,11 @@ async function getCooperative(coopname) {
return cooperative;
}
async function registerBlockchainAccount(user: IUser, orderData: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit) {
const eos = await getInstance(process.env.REGISTRATOR_WIF);
async function registerBlockchainAccount(user: IUser, order: IOrder) {
const eos = await getInstance(config.coopname);
const actions = [] as any;
// Создаем newaccount
const newaccount: RegistratorContract.Actions.CreateAccount.ICreateAccount = {
registrator: process.env.COOPNAME as string,
coopname: process.env.COOPNAME as string,
@@ -85,6 +132,20 @@ async function registerBlockchainAccount(user: IUser, orderData: GatewayContract
meta: '',
};
actions.push({
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.CreateAccount.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data: newaccount,
});
// Создаем registerUserData
const registerUserData: RegistratorContract.Actions.RegisterUser.IRegistrerUser = {
coopname: process.env.COOPNAME as string,
registrator: process.env.COOPNAME as string,
@@ -92,61 +153,206 @@ async function registerBlockchainAccount(user: IUser, orderData: GatewayContract
type: user.type,
};
//не следует создавать аккаунт в случаях, если он уже есть у пользователя
actions.push({
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.RegisterUser.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data: registerUserData,
});
// Проверяем наличие заявления на вступление и создаем joinCooperativeData
const statement = await TempDocument.findOne({ username: user.username, type: tempdocType.JoinStatement });
if (!statement) throw new ApiError(httpStatus.BAD_REQUEST, 'Не найдено заявление на вступление');
const joinCooperativeData: RegistratorContract.Actions.JoinCooperative.IJoinCooperative = {
coopname: process.env.COOPNAME as string,
registrator: process.env.COOPNAME as string,
username: user.username,
document: { ...user.statement, meta: JSON.stringify(user.statement.meta) },
document: { ...statement.document, meta: JSON.stringify(statement.document.meta) },
};
actions.push({
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.JoinCooperative.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data: joinCooperativeData,
});
// Создаем createDeposit
const createDeposit: GatewayContract.Actions.CreateDeposit.ICreateDeposit = {
coopname: config.coopname,
username: user.username,
type: 'registration',
quantity: order.quantity,
deposit_id: order.order_num as number,
};
actions.push({
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CreateDeposit.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data: createDeposit,
});
// Создаем completeDeposit
const completeDeposit: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit = {
coopname: config.coopname,
admin: config.coopname,
deposit_id: order.order_num as number,
memo: '',
};
actions.push({
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteDeposit.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data: completeDeposit,
});
// Проверяем наличие соглашения на кошелек и создаем walletAgreementData
const walletAgreement = await TempDocument.findOne({ username: user.username, type: tempdocType.WalletAgreement });
if (!walletAgreement) throw new ApiError(httpStatus.BAD_REQUEST, 'Не найдено заявление на вступление');
const walletAgreementData: SovietContract.Actions.Agreements.SendAgreement.ISendAgreement = {
coopname: process.env.COOPNAME as string,
administrator: process.env.COOPNAME as string,
username: user.username,
agreement_type: 'wallet',
document: { ...walletAgreement.document, meta: JSON.stringify(walletAgreement.document.meta) },
};
actions.push({
account: SovietContract.contractName.production,
name: SovietContract.Actions.Agreements.SendAgreement.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data: walletAgreementData,
});
// Проверяем наличие соглашения по ЭЦП и создаем signatureAgreementData
const signatureAgreement = await TempDocument.findOne({ username: user.username, type: tempdocType.SignatureAgreement });
if (!signatureAgreement) throw new ApiError(httpStatus.BAD_REQUEST, 'Не найдено соглашение о правилах использования ЭЦП');
const signatureAgreementData: SovietContract.Actions.Agreements.SendAgreement.ISendAgreement = {
coopname: process.env.COOPNAME as string,
administrator: process.env.COOPNAME as string,
username: user.username,
agreement_type: 'signature',
document: { ...signatureAgreement.document, meta: JSON.stringify(signatureAgreement.document.meta) },
};
actions.push({
account: SovietContract.contractName.production,
name: SovietContract.Actions.Agreements.SendAgreement.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data: signatureAgreementData,
});
// Проверяем наличие соглашения о конфиденциальности и создаем privacyAgreementData
const privacyAgreement = await TempDocument.findOne({ username: user.username, type: tempdocType.PrivacyAgreement });
if (!privacyAgreement) throw new ApiError(httpStatus.BAD_REQUEST, 'Не найдено соглашение о политике конфиденциальности');
const privacyAgreementData: SovietContract.Actions.Agreements.SendAgreement.ISendAgreement = {
coopname: process.env.COOPNAME as string,
administrator: process.env.COOPNAME as string,
username: user.username,
agreement_type: 'privacy',
document: { ...privacyAgreement.document, meta: JSON.stringify(privacyAgreement.document.meta) },
};
actions.push({
account: SovietContract.contractName.production,
name: SovietContract.Actions.Agreements.SendAgreement.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data: privacyAgreementData,
});
// Проверяем наличие пользовательского соглашения и создаем userAgreementData
const userAgreement = await TempDocument.findOne({ username: user.username, type: tempdocType.UserAgreement });
if (!userAgreement) throw new ApiError(httpStatus.BAD_REQUEST, 'Не найдено подписанное пользовательское соглашение');
const userAgreementData: SovietContract.Actions.Agreements.SendAgreement.ISendAgreement = {
coopname: process.env.COOPNAME as string,
administrator: process.env.COOPNAME as string,
username: user.username,
agreement_type: 'user',
document: { ...userAgreement.document, meta: JSON.stringify(userAgreement.document.meta) },
};
actions.push({
account: SovietContract.contractName.production,
name: SovietContract.Actions.Agreements.SendAgreement.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data: userAgreementData,
});
const result = await eos.transact(
{
actions,
},
{
blocksBehind: 3,
expireSeconds: 30,
}
);
// console.dir(result, { depth: null });
}
async function createBoard(data: SovietContract.Actions.Boards.CreateBoard.ICreateboard) {
const eos = await getInstance(config.coopname);
// console.log('data: ', data);
const actions = [
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.CreateAccount.actionName,
account: SovietContract.contractName.production,
name: SovietContract.Actions.Boards.CreateBoard.actionName,
authorization: [
{
actor: process.env.COOPNAME as string,
actor: config.coopname,
permission: 'active',
},
],
data: newaccount,
},
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.RegisterUser.actionName, //reguser
authorization: [
{
actor: process.env.COOPNAME as string,
permission: 'active',
},
],
data: registerUserData,
},
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.JoinCooperative.actionName,
authorization: [
{
actor: process.env.COOPNAME as string,
permission: 'active',
},
],
data: joinCooperativeData,
},
{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteDeposit.actionName,
authorization: [
{
actor: process.env.COOPNAME as string,
permission: 'active',
},
],
data: orderData,
data,
},
];
const result = await eos.transact(
await eos.transact(
{
actions,
},
@@ -158,11 +364,11 @@ async function registerBlockchainAccount(user: IUser, orderData: GatewayContract
}
async function createOrder(data) {
const eos = await getInstance(process.env.REGISTRATOR_WIF);
const eos = await getInstance(config.coopname);
const actions = [
{
account: process.env.GATEWAY_CONTRACT as string,
account: GatewayContract.contractName.production,
name: 'deposit',
authorization: [
{
@@ -184,25 +390,51 @@ async function createOrder(data) {
}
);
const order_id = getInternalAction(result, 'newdepositid').id;
const order_num = getInternalAction(result, 'newdepositid').id;
return order_id;
return order_num;
}
async function completeOrder(data: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit) {
const eos = await getInstance(process.env.REGISTRATOR_WIF);
async function completeDeposit(order: IOrder) {
const eos = await getInstance(config.coopname);
const createDeposit: GatewayContract.Actions.CreateDeposit.ICreateDeposit = {
coopname: config.coopname,
username: order.username,
type: 'deposit',
quantity: order.quantity,
deposit_id: order.order_num as number,
};
const completeDeposit: GatewayContract.Actions.CompleteDeposit.ICompleteDeposit = {
coopname: config.coopname,
admin: config.coopname,
deposit_id: order.order_num as number,
memo: '',
};
const actions = [
{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CreateDeposit.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data: createDeposit,
},
{
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.CompleteDeposit.actionName,
authorization: [
{
actor: process.env.COOPNAME as string,
actor: config.coopname,
permission: 'active',
},
],
data,
data: completeDeposit,
},
];
@@ -218,11 +450,11 @@ async function completeOrder(data: GatewayContract.Actions.CompleteDeposit.IComp
}
async function failOrder(data) {
const eos = await getInstance(process.env.REGISTRATOR_WIF);
const eos = await getInstance(config.coopname);
const actions = [
{
account: process.env.GATEWAY_CONTRACT as string,
account: GatewayContract.contractName.production,
name: 'dpfail',
authorization: [
{
@@ -245,10 +477,114 @@ async function failOrder(data) {
);
}
export async function addUser(data: RegistratorContract.Actions.AddUser.IAddUser) {
const eos = await getInstance(config.coopname);
console.log('data:', data);
const actions = [
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.AddUser.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data,
},
];
await eos.transact(
{
actions,
},
{
blocksBehind: 3,
expireSeconds: 30,
}
);
}
export async function changeKey(data: RegistratorContract.Actions.ChangeKey.IChangeKey) {
const eos = await getInstance(config.coopname);
const actions = [
{
account: RegistratorContract.contractName.production,
name: RegistratorContract.Actions.ChangeKey.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data,
},
];
await eos.transact(
{
actions,
},
{
blocksBehind: 3,
expireSeconds: 30,
}
);
}
export async function cancelOrder(data: GatewayContract.Actions.RefundDeposit.IRefundDeposit) {
const action: IBCAction<GatewayContract.Actions.RefundDeposit.IRefundDeposit> = {
account: GatewayContract.contractName.production,
name: GatewayContract.Actions.RefundDeposit.actionName,
authorization: [
{
actor: config.coopname,
permission: 'active',
},
],
data,
};
return await transact([action]);
}
export async function powerUp(username: string, quantity: string): Promise<void> {
const data: SystemContract.Actions.Powerup.IPowerup = {
payer: username,
receiver: username,
days: 1,
payment: quantity,
transfer: false,
};
const actions = [
{
account: 'eosio',
name: 'powerup',
authorization: [
{
actor: username,
permission: 'active',
},
],
data,
},
];
try {
const result = await transact(actions);
console.log('Транзакция powerup выполнена:', result);
} catch (error) {
console.error('Ошибка при выполнении транзакции powerup:', error);
throw error;
}
}
async function getSoviet(coopname) {
const api = await getApi();
const soviet = (await lazyFetch(api, process.env.SOVIET_CONTRACT, coopname, 'boards'))[0];
const soviet = (await lazyFetch(api, SovietContract.contractName.production, coopname, 'boards'))[0];
return soviet;
}
@@ -256,7 +592,7 @@ async function getSoviet(coopname) {
async function fetchAllParticipants() {
const api = await getApi();
const participants = await lazyFetch(api, process.env.SOVIET_CONTRACT, process.env.COOPNAME, 'participants');
const participants = await lazyFetch(api, SovietContract.contractName.production, process.env.COOPNAME, 'participants');
return participants;
}
@@ -269,6 +605,10 @@ export {
createOrder,
getCooperative,
failOrder,
completeOrder,
completeDeposit,
getSoviet,
getBlockchainInfo,
getBlockchainAccount,
hasActiveKey,
createBoard,
};
@@ -1,27 +1,28 @@
import { getActions } from '../utils/getFetch';
import { generator } from './data.service';
import { generator } from './document.service';
import { userService, blockchainService } from './index';
import { Cooperative, SovietContract } from 'cooptypes';
import { Cooperative, RegistratorContract, SovietContract } from 'cooptypes';
import ApiError from '../utils/ApiError';
import httpStatus from 'http-status';
import logger from '../config/logger';
export const loadAgenda = async (coopname: string): Promise<Cooperative.Documents.IAgenda[]> => {
export const loadAgenda = async (coopname: string): Promise<Cooperative.Document.IAgenda[]> => {
const api = await blockchainService.getApi();
const decisions = (await blockchainService.lazyFetch(
api,
process.env.SOVIET_CONTRACT as string,
SovietContract.contractName.production as string,
coopname,
'decisions'
)) as SovietContract.Tables.Decisions.IDecision[];
const agenda = [] as Cooperative.Documents.IAgenda[];
const agenda = [] as Cooperative.Document.IAgenda[];
for (const table of decisions) {
const action = (
await getActions(`${process.env.SIMPLE_EXPLORER_API}/get-actions`, {
filter: JSON.stringify({
account: process.env.SOVIET_CONTRACT,
account: SovietContract.contractName.production,
name: SovietContract.Actions.Registry.NewSubmitted.actionName,
receiver: process.env.COOPNAME,
'data.decision_id': String(table.id),
@@ -39,7 +40,7 @@ export const loadAgenda = async (coopname: string): Promise<Cooperative.Document
export const loadStaff = async (coopname) => {
const api = await blockchainService.getApi();
const staff = await blockchainService.lazyFetch(api, process.env.SOVIET_CONTRACT, coopname, 'staff');
const staff = await blockchainService.lazyFetch(api, SovietContract.contractName.production, coopname, 'staff');
for (const staf of staff) {
const user = await userService.getUserByUsername(staf.username);
@@ -63,11 +64,32 @@ export const loadInfo = async (coopname: string) => {
export const loadContacts = async (coopname: string) => {
const cooperative: Cooperative.Model.ICooperativeData | null = await generator.constructCooperative(coopname);
if (!cooperative) throw new Error('Кооператив не найден');
if (!cooperative) throw new ApiError(httpStatus.NOT_FOUND, 'Кооператив не найден');
const announce = cooperative?.announce
? JSON.parse(cooperative.announce)
: { phone: cooperative?.phone, email: cooperative?.email };
const api = await blockchainService.getApi();
const coopAccount = (
await blockchainService.lazyFetch(
api,
RegistratorContract.contractName.production,
RegistratorContract.contractName.production,
RegistratorContract.Tables.Accounts.tableName,
coopname,
coopname,
1
)
)[0];
if (!coopAccount) throw new ApiError(httpStatus.NOT_FOUND, 'Аккаунт не найден');
let announce: Cooperative.Users.IAccountMeta = { phone: cooperative?.phone, email: cooperative?.email };
if (coopAccount.meta) {
try {
announce = JSON.parse(coopAccount.meta);
} catch (e: any) {
logger.warn(`Ошибка при получении контактов: ${e.message}`);
}
}
return {
full_name: cooperative?.full_name,
@@ -1,15 +1,9 @@
import eosjsecc from 'eosjs-ecc';
import ApiError from '../utils/ApiError';
import { getActions, getTables } from '../utils/getFetch';
import * as coopService from './coop.service';
const { verify, sha256 } = eosjsecc;
import { getActions } from '../utils/getFetch';
import config from '../config/config';
import { Generator } from 'coopdoc-generator-ts';
import type { IGenerate, IGetDocuments } from '../types';
import type { IGenerate } from '../types';
import { Cooperative, SovietContract } from 'cooptypes';
import { User } from '../models';
import { IUser } from '../models/user.model';
export const generator = new Generator();
@@ -17,6 +11,10 @@ export const connectGenerator = async () => {
await generator.connect(config.mongoose.url);
};
export const diconnectGenerator = async () => {
await generator.disconnect();
};
export const generateDocument = async (options: IGenerate) => {
return await generator.generate(options);
};
@@ -24,19 +22,27 @@ export const generateDocument = async (options: IGenerate) => {
// Шаг 1: Создание новой функции для сборки complexDocument
export async function buildComplexDocument(
raw_action_document: Cooperative.Blockchain.IAction
): Promise<Cooperative.Documents.IComplexDocument> {
let statement = {} as Cooperative.Documents.IComplexStatement;
let decision = {} as Cooperative.Documents.IComplexDecision;
let act = {} as Cooperative.Documents.IComplexAct;
): Promise<Cooperative.Document.IComplexDocument> {
let statement = {} as Cooperative.Document.IComplexStatement;
let decision = {} as Cooperative.Document.IComplexDecision;
const act = {} as Cooperative.Document.IComplexAct;
const links: Cooperative.Document.IGeneratedDocument[] = [];
const raw_document = raw_action_document.data as SovietContract.Actions.Registry.NewSubmitted.INewSubmitted;
// Готовим заявления
{
const document = await generator.getDocument({ hash: raw_document.document.hash });
if (document && document.meta.links && Array.isArray(document.meta.links))
for (const link of document.meta.links) {
const linked_document = await generator.getDocument({ hash: link });
links.push(linked_document);
}
const user = await User.findOne({ username: raw_document.username });
if (user) {
if (user && user.type != 'service') {
const user_data = await user?.getPrivateData();
const action: Cooperative.Blockchain.IExtendedAction = {
@@ -45,8 +51,6 @@ export async function buildComplexDocument(
};
statement = { action, document };
} else {
// throw new ApiError(400, 'Ошибка, один из пользователей не найден. Обратитесь в поддержку.');
}
}
@@ -57,7 +61,7 @@ export async function buildComplexDocument(
const decision_action = (
await getActions(`${process.env.SIMPLE_EXPLORER_API}/get-actions`, {
filter: JSON.stringify({
account: process.env.SOVIET_CONTRACT,
account: SovietContract.contractName.production,
name: SovietContract.Actions.Registry.NewDecision.actionName,
receiver: process.env.COOPNAME,
'data.decision_id': String(raw_document.decision_id),
@@ -71,8 +75,6 @@ export async function buildComplexDocument(
const user = await User.findOne({ username: decision_action?.data?.username });
if (user) {
const user_data = user.getPrivateData();
decision_extended_action = {
...decision_action,
user: (await user?.getPrivateData()) || null,
@@ -80,6 +82,12 @@ export async function buildComplexDocument(
const document = await generator.getDocument({ hash: decision_action?.data?.document?.hash });
if (document && document.meta.links && Array.isArray(document.meta.links))
for (const link of document.meta.links) {
const linked_document = await generator.getDocument({ hash: link });
links.push(linked_document);
}
decision = {
document,
action: decision_extended_action,
@@ -93,31 +101,46 @@ export async function buildComplexDocument(
}
// Готовим акты
const acts: Cooperative.Documents.IComplexAct[] = [];
const acts: Cooperative.Document.IComplexAct[] = [];
return { statement, decision, acts };
return { statement, decision, acts, links };
}
function toDotNotation(obj, prefix = '') {
return Object.keys(obj).reduce((acc, key) => {
const value = obj[key];
const newKey = prefix ? `${prefix}.${key}` : key;
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
Object.assign(acc, toDotNotation(value, newKey));
} else {
acc[newKey] = value;
}
return acc;
}, {});
}
export const queryDocuments = async (
type: string,
filter: any,
page: number = 1,
limit: number = 100
): Promise<Cooperative.Documents.IGetComplexDocuments> => {
page = 1,
limit = 100
): Promise<Cooperative.Document.IGetComplexDocuments> => {
const actions = await getActions<SovietContract.Actions.Registry.NewResolved.INewResolved>(
`${process.env.SIMPLE_EXPLORER_API}/get-actions`,
{
filter: JSON.stringify({
account: process.env.SOVIET_CONTRACT,
name: SovietContract.Actions.Registry.NewResolved.actionName,
receiver: process.env.COOPNAME,
...filter,
account: SovietContract.contractName.production,
name: type, //< newresolved | newsubmitted
...toDotNotation(filter),
}),
page,
limit,
}
);
let response: Cooperative.Documents.IGetComplexDocuments = {
const response: Cooperative.Document.IGetComplexDocuments = {
results: [],
page,
limit,
@@ -126,32 +149,8 @@ export const queryDocuments = async (
for (const raw_action_document of actions.results) {
const complexDocument = await buildComplexDocument(raw_action_document);
if (complexDocument.decision.action) response.results.push(complexDocument);
if (complexDocument.statement.action) response.results.push(complexDocument);
}
return response;
};
export const getDocuments = async (account) => {
// let data = await getActions('/v2/history/get_actions', {
// "filter": "soviettest1:statement",
// "account": account,
// })
// for (const action of data.actions) {
// action.decision = (await getActions('/v2/history/get_actions', {
// "account": account,
// "filter": "soviettest1:decision",
// "@decision.decision_id": action.act.data.decision_id,
// }))?.actions?.[0]
// if (action.act.data.action == 'joincoop') {
// try {
// action.verified = await verify(action.act.data.statement.sign, action.act.data.statement.hash, action.act.data.statement.pkey)
// } catch(e: any){
// console.error(e)
// action.verified = false
// action.verify_message = e.message
// }
// }
// }
// return data.actions
};
@@ -2,9 +2,9 @@ import { createTransport } from 'nodemailer';
import config from '../config/config';
import logger from '../config/logger';
const { email, env } = config
const { email, env } = config;
const transport = createTransport(email.smtp);
export const transport = createTransport(email.smtp);
/* istanbul ignore next */
if (env !== 'test') {
@@ -21,7 +21,7 @@ if (env !== 'test') {
* @param {string} text
* @returns {Promise}
*/
const sendEmail = async (to, subject, text) => {
export const sendEmail = async (to, subject, text) => {
const msg = { from: email.from, to, subject, text };
await transport.sendMail(msg);
};
@@ -32,13 +32,33 @@ const sendEmail = async (to, subject, text) => {
* @param {string} token
* @returns {Promise}
*/
const sendResetPasswordEmail = async (to, token) => {
const subject = 'Reset password';
// replace this url with the link to the reset password page of your front-end app
const resetPasswordUrl = `http://link-to-app/reset-password?token=${token}`;
const text = `Dear user,
To reset your password, click on this link: ${resetPasswordUrl}
If you did not request any password resets, then ignore this email.`;
export const sendResetKeyEmail = async (to, token) => {
const subject = 'Восстановление доступа';
const resetPasswordUrl = `${config.base_url}/#/${config.coopname}/auth/reset-key?token=${token}`;
const text = `Мы получили запрос на перевыпуск приватного ключа,
Для перевыпуска нажмите на ссылку: ${resetPasswordUrl}. Время действия ссылки - 10 минут.
Если вы не запрашивали перевыпуск ключа - проигнорируйте это сообщение.`;
await sendEmail(to, subject, text);
};
/**
* Send reset password email
* @param {string} to
* @param {string} token
* @returns {Promise}
*/
export const sendInviteEmail = async (to, token) => {
const subject = 'Приглашение в Цифровой Кооператив';
const inviteUrl = `${config.base_url}/#/${config.coopname}/auth/reset-key?token=${token}`;
const text = `Вам отправлено приглашение на подключение к Цифровому Кооперативу в качестве действующего пайщика.
Для того, чтобы воспользоваться приглашением и получить ключ доступа, пожалуйста, нажмите на ссылку: ${inviteUrl}
Время действия ссылки - 24 часа.`;
await sendEmail(to, subject, text);
};
@@ -48,7 +68,7 @@ If you did not request any password resets, then ignore this email.`;
* @param {string} token
* @returns {Promise}
*/
const sendVerificationEmail = async (to, token) => {
export const sendVerificationEmail = async (to, token) => {
const subject = 'Email Verification';
// replace this url with the link to the email verification page of your front-end app
const verificationEmailUrl = `http://link-to-app/verify-email?token=${token}`;
@@ -57,10 +77,3 @@ To verify your email, click on this link: ${verificationEmailUrl}
If you did not create an account, then ignore this email.`;
await sendEmail(to, subject, text);
};
export {
transport,
sendEmail,
sendResetPasswordEmail,
sendVerificationEmail,
};
+8 -1
View File
@@ -4,6 +4,13 @@ export * as tokenService from './token.service';
export * as userService from './user.service';
export * as blockchainService from './blockchain.service';
export * as orderService from './order.service';
export * as dataService from './data.service';
export * as documentService from './document.service';
export * as coopService from './coop.service';
export * as wsService from './ws.service';
export * as systemService from './system.service';
export * as participantService from './participant.service';
export * as redisService from './redis.service';
export * as methodService from './method.service';
export * as paymentService from './payment.service';
export * as settingService from './setting.service';
export * as pluginService from './plugin.service';
@@ -0,0 +1,15 @@
import type { Cooperative } from 'cooptypes';
import { generator } from './document.service';
import type { FilterQuery } from 'mongoose';
export const savePaymentMethod = async (data: Cooperative.Payments.IPaymentData) => {
return await generator.save('paymentMethod', data);
};
export const deletePaymentMethod = async (filter: FilterQuery<Cooperative.Payments.IPaymentData>) => {
return await generator.del('paymentMethod', filter as any);
};
export const listPaymentMethods = async (filter: FilterQuery<Cooperative.Payments.IPaymentData>) => {
return await generator.list('paymentMethod', filter as any);
};
@@ -1 +1 @@
export default {};

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