Compare commits

...

243 Commits

Author SHA1 Message Date
Dot.L a1bdc048a7 Merge pull request #149 from APIParkLab/feature/aibug-fix
add default router when create new rest service
2024-12-16 14:43:26 +08:00
Liujian 0a1b08157d add default router when create new rest service 2024-12-16 14:31:52 +08:00
ningyv d36c66371f Merge pull request #147 from APIParkLab/fix/fixbug-cx
fix: consumer permission
2024-12-13 20:02:45 +08:00
ningyv b7bb409e96 fix: consumer permission 2024-12-13 20:00:38 +08:00
Dot.L 517007c941 Merge pull request #146 from APIParkLab/feature/aibug-fix
fix aksk bug
2024-12-13 19:41:10 +08:00
Liujian 4c685a9ec6 fix aksk bug 2024-12-13 18:52:21 +08:00
Dot.L 1aca2099de Merge pull request #145 from APIParkLab/feature/aibug-fix
fix ai provider upstream bug
2024-12-10 17:26:47 +08:00
Liujian a93e5b4ff8 fix ai provider upstream bug 2024-12-10 17:24:51 +08:00
ningyv 85d25bebe2 Merge branch 'main' of github.com:APIParkLab/APIPark 2024-12-10 15:34:44 +08:00
lichunxian 9fa43ccc00 Merge branch 'fix/certificatePermission' into 'main'
fix: table-permission

See merge request apipark/APIPark!124
2024-12-10 15:17:11 +08:00
ningyv c2a11050dd fix: table-permission 2024-12-10 15:16:19 +08:00
Dot.L 080bfc3a44 Merge pull request #143 from eltociear/patch-1
chore: update index.tsx
2024-12-09 21:48:14 +08:00
Liujian f6956ddeca Merge remote-tracking branch 'origin/main' into main-github-pro 2024-12-09 21:42:09 +08:00
Liujian 9f56fa5e14 update init log 2024-12-09 21:32:57 +08:00
lichunxian ccc39b95de Merge branch 'fix/certificatePermission' into 'main'
fix: implement certificate popup permission handling

See merge request apipark/APIPark!123
2024-12-09 15:22:29 +08:00
ningyv 9a2782e54b fix: implement certificate popup permission handling 2024-12-09 15:18:59 +08:00
Liujian 22455e2301 Automatically publish policies and logs during cluster initialization 2024-12-09 00:43:05 +08:00
Liujian 8ed2c84b68 update loki publish 2024-12-09 00:17:50 +08:00
Liujian ccd2a209e2 update .gitignore 2024-12-06 18:48:00 +08:00
lichunxian baf8ed4830 Merge branch 'feature/dataLogPage' into 'main'
fix: improve column width adjustment and optimize date picker performance

See merge request apipark/APIPark!122
2024-12-06 18:03:16 +08:00
ningyv dedb586daf fix: improve column width adjustment and optimize date picker performance 2024-12-06 18:02:44 +08:00
刘健 21cd823791 Merge branch 'feature/data-mask' into 'main'
update publish problem

See merge request apipark/APIPark!121
2024-12-06 16:13:23 +08:00
Liujian c8ab65ef1b update publish problem 2024-12-06 16:12:49 +08:00
lichunxian f1c16fd992 Merge branch 'feature/dataLogPage' into 'main'
Feature/data log page

See merge request apipark/APIPark!120
2024-12-06 15:25:51 +08:00
ningyv 52035341f6 fix: refine time range calculation with second-level precision 2024-12-06 15:24:06 +08:00
ningyv aa62d44717 fix: resolve subscriber permissions 2024-12-06 14:22:59 +08:00
lichunxian a072d1fc8d Merge branch 'feature/dataLogPage' into 'main'
feat: integrate global policy API and implement data log page

See merge request apipark/APIPark!119
2024-12-06 11:51:13 +08:00
ningyv 38a00570d0 feat: integrate global policy API and implement data log page 2024-12-06 11:50:32 +08:00
刘健 43283b9da3 Merge branch 'feature/data-mask' into 'main'
update service publish

See merge request apipark/APIPark!118
2024-12-06 11:32:15 +08:00
Liujian ef82cdbed6 update service publishing difference comparison field 2024-12-06 11:31:58 +08:00
Liujian 2bafe6f31f update service publish 2024-12-06 11:02:40 +08:00
刘健 3eb4f98fd8 Merge branch 'feature/data-mask' into 'main'
add service strategy log

See merge request apipark/APIPark!117
2024-12-06 10:49:21 +08:00
Liujian bb5acad033 add service strategy log 2024-12-06 10:47:47 +08:00
刘健 b8308a446b Merge branch 'feature/data-mask' into 'main'
update service publish

See merge request apipark/APIPark!116
2024-12-06 10:40:20 +08:00
Liujian c86f99ce45 update service publish 2024-12-06 10:39:59 +08:00
lichunxian 952c519e45 Merge branch 'feature/dataLogPage' into 'main'
feat: integrate global policy API and implement data log page

See merge request apipark/APIPark!115
2024-12-05 18:53:51 +08:00
ningyv 07d97fa0bf feat: integrate global policy API and implement data log page 2024-12-05 18:53:11 +08:00
刘健 b0defedf04 Merge branch 'feature/data-mask' into 'main'
fix log bug:Keyword query failed

See merge request apipark/APIPark!114
2024-12-05 18:17:49 +08:00
Liujian edc2fccdeb fix log bug:Keyword query failed 2024-12-05 18:17:29 +08:00
刘健 a75b8a3f13 Merge branch 'feature/data-mask' into 'main'
fix logs bug

See merge request apipark/APIPark!113
2024-12-05 17:21:46 +08:00
Liujian 5aab5f7913 fix logs bug 2024-12-05 17:20:11 +08:00
刘健 9ab7989c8b Merge branch 'feature/data-mask' into 'main'
fix log bug

See merge request apipark/APIPark!112
2024-12-05 17:15:04 +08:00
Liujian e3e11d740a fix log bug 2024-12-05 17:14:26 +08:00
刘健 4bae2edc49 Merge branch 'feature/data-mask' into 'main'
update strategy publish bug

See merge request apipark/APIPark!111
2024-12-05 15:44:22 +08:00
Liujian 4eaa47ca25 update strategy publish bug 2024-12-05 15:43:42 +08:00
刘健 e01f596525 Merge branch 'feature/data-mask' into 'main'
update log label

See merge request apipark/APIPark!109
2024-12-05 15:06:28 +08:00
Liujian 912e8d0d04 update log label 2024-12-05 15:04:43 +08:00
刘健 570c80af91 Merge branch 'feature/data-mask' into 'main'
Feature/data mask

See merge request apipark/APIPark!108
2024-12-05 14:50:19 +08:00
Liujian 7aa0ec0d67 Merge remote-tracking branch 'origin/main' into feature/data-mask
# Conflicts:
#	frontend/packages/core/src/pages/policy/dataMasking/DataMasking.tsx
2024-12-05 14:50:00 +08:00
Liujian 2fea6cb622 update eosc version 2024-12-05 14:43:24 +08:00
Liujian 2195ff900f data mask log commit 2024-12-05 14:39:57 +08:00
Ikko Eltociear Ashimine 836c7699b8 chore: update index.tsx
protocal -> protocol
2024-12-04 15:54:09 +09:00
Liujian 72ed6c814e Merge remote-tracking branch 'github-pro/feature/v1.3/mj' into feature/data-mask 2024-12-03 16:07:13 +08:00
ningyv c33b070509 Merge branch 'feature/v1.3/mj' of github.com:APIParkLab/APIPark into feature/v1.3/mj 2024-11-29 18:39:20 +08:00
ningyv de12d5686c feature: dataMask logs dialog 2024-11-29 18:39:13 +08:00
lichunxian c55a8ac805 Merge branch 'fix/copyIssue' into 'main'
Fix/copy issue

See merge request apipark/APIPark!107
2024-11-29 10:39:28 +08:00
杨梦洁 df3626f3f0 Merge branch 'feature/v1.3/mj' into 'main'
fix: Dashboard Table Height

See merge request apipark/APIPark!106
2024-11-29 10:36:45 +08:00
杨梦洁 28bef97faa fix: Dashboard Table Height 2024-11-29 10:35:32 +08:00
ningyv 9897b6e9dc fix: Fix the issue where copying the URL doesn't work 2024-11-29 10:22:40 +08:00
ningyv 9af6963901 fix: Fixed side navigation disappearing 2024-11-29 10:08:06 +08:00
lichunxian c2d3ebecda Merge branch 'fix/fix-table' into 'main'
Fix/fix table

See merge request apipark/APIPark!105
2024-11-29 09:55:26 +08:00
ningyv ba543311fc Merge branch 'main' of http://gitlab.eolink.com/apipark/APIPark 2024-11-29 09:45:55 +08:00
ningyv 87b8dda97b Fix Set the header width 2024-11-29 09:41:50 +08:00
ningyv e7facf5686 Fix icon issues 2024-11-28 19:15:02 +08:00
刘健 4dd57837c5 Merge branch 'feature/data-mask' into 'main'
update transaction

See merge request apipark/APIPark!104
2024-11-28 18:41:56 +08:00
Liujian d4ebc68e30 update transaction 2024-11-28 18:40:00 +08:00
Maggie 1f8e089e51 Merge pull request #141 from APIParkLab/feature/v1.3/mj
fix: get service remote option
2024-11-28 18:39:17 +08:00
杨梦洁 ce6f463fe8 Merge branch 'feature/v1.3/mj' into 'main'
fix: get service remote option

See merge request apipark/APIPark!103
2024-11-28 18:38:51 +08:00
杨梦洁 a37fe1d794 fix: get service remote option 2024-11-28 18:38:12 +08:00
刘健 818d1ec6bf Merge branch 'feature/data-mask' into 'main'
fix strategy bug

See merge request apipark/APIPark!102
2024-11-28 18:35:20 +08:00
Liujian fc4a5f7e28 fix strategy bug 2024-11-28 18:34:15 +08:00
Maggie 0e3568b584 Merge pull request #140 from APIParkLab/feature/v1.3/mj
fix: service data-masking layout
2024-11-28 18:24:44 +08:00
杨梦洁 3617e4fe29 Merge branch 'feature/v1.3/mj' into 'main'
fix: service data-masking layout

See merge request apipark/APIPark!101
2024-11-28 18:22:44 +08:00
刘健 f2fddc1727 Merge branch 'feature/data-mask' into 'main'
Feature/data mask

See merge request apipark/APIPark!100
2024-11-28 18:19:38 +08:00
Liujian 3b7204f1a6 openapi取消登录校验 2024-11-28 18:19:08 +08:00
杨梦洁 6646bb1e56 fix: service data-masking layout 2024-11-28 18:14:02 +08:00
Liujian c27533f802 Merge remote-tracking branch 'github-pro/main' into feature/data-mask 2024-11-28 17:35:53 +08:00
Maggie 613a47c181 Merge pull request #139 from APIParkLab/feature/v1.3/mj
Feature/v1.3/mj
2024-11-28 17:01:32 +08:00
杨梦洁 90138a142b Merge branch 'feature/v1.3/mj' into 'main'
fix: Modify Context Arct

See merge request apipark/APIPark!99
2024-11-28 17:01:11 +08:00
杨梦洁 4bf8db4898 fix: Modify Context Arct 2024-11-28 16:59:51 +08:00
杨梦洁 4f887f7204 Merge branch 'feature/v1.3/mj' into 'main'
fix: add translation

See merge request apipark/APIPark!98
2024-11-28 13:49:43 +08:00
杨梦洁 14e17ccf2c fix: add translation 2024-11-28 13:47:31 +08:00
刘健 5975670b8c Merge branch 'feature/data-mask' into 'main'
add publish tip

See merge request apipark/APIPark!97
2024-11-28 11:17:36 +08:00
Liujian dbc4bc3343 add publish tip 2024-11-28 11:17:02 +08:00
刘健 ada7635703 Merge branch 'feature/data-mask' into 'main'
fix rest service publish

See merge request apipark/APIPark!95
2024-11-28 10:49:16 +08:00
Maggie 943a77f718 Merge pull request #137 from APIParkLab/feature/v1.3/mj
fix: role list scroll bug and add translation
2024-11-28 10:47:44 +08:00
杨梦洁 ef02c11efa Merge branch 'feature/v1.3/mj' into 'main'
fix: role list scroll bug and add translation

See merge request apipark/APIPark!96
2024-11-28 10:41:45 +08:00
杨梦洁 8c166dae9b fix: role list scroll bug and add translation 2024-11-28 10:40:43 +08:00
Liujian b23da78c26 fix rest service publish 2024-11-27 20:18:23 +08:00
刘健 93ac7310e8 Merge branch 'feature/data-mask' into 'main'
finish service publish

See merge request apipark/APIPark!94
2024-11-27 19:47:16 +08:00
Liujian 9376acc456 finish service publish 2024-11-27 19:46:52 +08:00
Maggie b70a1f9a51 Merge pull request #136 from APIParkLab/feature/v1.3/mj
Feature/v1.3/mj
2024-11-27 19:22:06 +08:00
杨梦洁 ac90a134b4 Merge branch 'feature/v1.3/mj' into 'main'
fix: data-masking and integration bugs

See merge request apipark/APIPark!93
2024-11-27 19:21:58 +08:00
杨梦洁 96bd1cf9f6 fix: data-masking and integration bugs 2024-11-27 19:20:47 +08:00
刘健 960e37a81a Merge branch 'feature/data-mask' into 'main'
Feature/data mask

See merge request apipark/APIPark!92
2024-11-27 19:05:28 +08:00
Liujian 044bd550c9 update service publish rule 2024-11-27 19:05:11 +08:00
Liujian 0a9a903d1b Merge remote-tracking branch 'origin/main' into feature/data-mask 2024-11-27 18:30:46 +08:00
杨梦洁 84d7606e12 Merge branch 'feature/v1.3/mj' into 'main'
fix: data-masking bugs

See merge request apipark/APIPark!91
2024-11-27 18:22:41 +08:00
杨梦洁 febb64b8bb fix: data-masking bugs 2024-11-27 18:21:43 +08:00
刘健 932e433c46 Merge branch 'feature/data-mask' into 'main'
add open api swagger

See merge request apipark/APIPark!90
2024-11-27 13:52:15 +08:00
Liujian b7307cd36d add open api swagger 2024-11-27 13:51:55 +08:00
刘健 7a0f3efd83 Merge branch 'feature/data-mask' into 'main'
add strategy permit

See merge request apipark/APIPark!89
2024-11-27 11:47:22 +08:00
Liujian 28af1f691c add strategy permit 2024-11-27 11:46:56 +08:00
刘健 796bc7bc15 Merge branch 'feature/data-mask' into 'main'
add api: /simple/service/apis

See merge request apipark/APIPark!88
2024-11-27 00:35:11 +08:00
Liujian 8f06073783 add api: /simple/service/apis 2024-11-27 00:34:24 +08:00
刘健 7d6251b191 Merge branch 'feature/data-mask' into 'main'
Feature/data mask

See merge request apipark/APIPark!87
2024-11-26 23:50:14 +08:00
Liujian e13fff633e system setting add site_prefix 2024-11-26 23:49:44 +08:00
Liujian d984be4b85 finish data mask strategy 2024-11-26 23:44:00 +08:00
Liujian 213bdbd9d5 Merge remote-tracking branch 'github-pro/main' into feature/data-mask 2024-11-25 10:35:21 +08:00
杨梦洁 6a59d27b84 Merge branch 'feature/v1.3/mj' into 'main'
fix: Change Dashboard Fields

See merge request apipark/APIPark!86
2024-11-21 18:38:13 +08:00
杨梦洁 ad45ab2e82 fix: Change Dashboard Fields 2024-11-21 18:37:39 +08:00
Liujian 8982a63283 Merge remote-tracking branch 'origin/feature/v1.3/mj' into feature/data-mask 2024-11-21 17:36:57 +08:00
Liujian 1b1515a8bd Initial submission of data desensitization strategy backend 2024-11-21 17:36:13 +08:00
Maggie df50e13db0 Merge pull request #135 from APIParkLab/feature/v1.3/mj
Feature/v1.3/mj
2024-11-21 17:05:48 +08:00
杨梦洁 89d91c14c9 Merge branch 'feature/v1.3/mj' into 'main'
fix: Change Vite Config

See merge request apipark/APIPark!85
2024-11-21 16:43:53 +08:00
杨梦洁 3a57c609f7 fix: Change Vite Config 2024-11-21 16:42:32 +08:00
杨梦洁 2cd331ec50 Merge branch 'feature/v1.3/mj' into 'main'
fix: Change File Name

See merge request apipark/APIPark!84
2024-11-21 16:34:31 +08:00
杨梦洁 3fa02ec65c fix: Change File Name 2024-11-21 16:32:07 +08:00
杨梦洁 f33f1965b4 Merge branch 'feature/v1.3/mj' into 'main'
Feature/v1.3/mj

See merge request apipark/APIPark!83
2024-11-21 16:26:59 +08:00
杨梦洁 a70ecea02b fix: Change File Name 2024-11-21 16:25:18 +08:00
杨梦洁 8e68eb35f3 fix: Change file name 2024-11-21 16:20:52 +08:00
杨梦洁 2893331ff5 Merge branch 'main' into feature/v1.3/mj 2024-11-21 16:12:16 +08:00
杨梦洁 1a3d14cdd6 Merge branch 'feature/v1.3/mj' into 'main'
Feature/v1.3/mj

See merge request apipark/APIPark!82
2024-11-21 16:11:42 +08:00
杨梦洁 dce9a7addb feat: Complete static pages for Phase 1 of V1.3 2024-11-21 16:08:08 +08:00
Liujian f3e7487482 add gitlab-ci 2024-11-15 14:35:27 +08:00
Liujian 86c39237dc Merge remote-tracking branch 'github-pro/main' 2024-11-15 14:21:23 +08:00
Dot.L b5ad739b93 Merge pull request #134 from PeterDaveHelloKitchen/zh-TW
Improve zh-TW Traditional Chinese locale
2024-11-15 14:19:39 +08:00
杨梦洁 0b7f0405d5 feat: Merge MF with data-masking list 2024-11-15 13:42:30 +08:00
lcx 1ab56708a5 feat: Global/Service Policy Development, Add Service Details Integration Tab 2024-11-15 10:44:16 +08:00
杨梦洁 522489c9e9 feat: plugin system 2024-11-14 19:09:46 +08:00
Peter Dave Hello a9eb2a790f Improve zh-TW Traditional Chinese locale 2024-11-14 05:49:34 +08:00
maggieyyy a092ed1108 fix: Move plugin code to common 2024-11-06 14:26:38 +08:00
maggieyyy 503515281d Merge branch 'main' into feature/mf 2024-11-06 11:33:21 +08:00
maggieyyy 2326d4dfb5 feat: plugin system 2024-11-05 16:20:40 +08:00
Dot.L 42e8030cf7 Merge pull request #130 from APIParkLab/feature/permit
Feature/permit
2024-11-04 11:06:03 +08:00
Liujian 17b4ede566 Merge branch 'main-github' into feature/permit
# Conflicts:
#	go.mod
2024-11-04 11:05:30 +08:00
Liujian bbc3fea848 update go.mod 2024-11-04 11:03:40 +08:00
Liujian 82e46b872b Merge remote-tracking branch 'origin/main' into main-github 2024-11-04 11:00:05 +08:00
Liujian 0f6a091c73 fix service info no prefix 2024-11-04 10:59:13 +08:00
Liujian 4e87adb4b3 Merge remote-tracking branch 'origin/main' into main-github 2024-11-04 10:49:05 +08:00
杨梦洁 320a2b6cf8 Merge branch 'feature/v1.2' into 'main'
fix: Modify service inside access

See merge request apipark/APIPark!81
2024-11-04 10:46:21 +08:00
maggieyyy b216556867 fix: Modify service inside access 2024-11-04 10:44:30 +08:00
Liujian 7fbb98a2a9 Merge remote-tracking branch 'github-pro/main' into main-github 2024-11-01 19:03:17 +08:00
Liujian 461a8edbea update .gitignore 2024-11-01 19:01:50 +08:00
刘健 cdef179bed Merge branch 'feature/permit' into 'main'
Feature/permit

See merge request apipark/APIPark!80
2024-11-01 18:53:22 +08:00
Liujian a067388d79 update ai permit 2024-11-01 18:52:50 +08:00
杨梦洁 2d5e541593 Merge branch 'feature/v1.2' into 'main'
fix: Modify link access

See merge request apipark/APIPark!79
2024-11-01 18:42:40 +08:00
Maggie be40186ad3 Merge pull request #129 from APIParkLab/feature/v1.2
fix: Modify link access
2024-11-01 18:42:26 +08:00
maggieyyy c9ae05b22e fix: Modify link access 2024-11-01 18:41:16 +08:00
maggieyyy 2b874fe59f fix: Modify execute list 2024-11-01 18:17:16 +08:00
Liujian 9da5e5d6c0 Merge remote-tracking branch 'github-pro/main' into feature/permit 2024-11-01 18:05:14 +08:00
Liujian a6bfce2a5f Merge remote-tracking branch 'origin/main' into feature/permit 2024-11-01 17:59:30 +08:00
Maggie 8cbeabe917 Merge pull request #128 from APIParkLab/feature/v1.2
Feature/v1.2
2024-11-01 16:45:24 +08:00
杨梦洁 1db354077f Merge branch 'feature/v1.2' into 'main'
fix: Modify consumer list reload and subscriber option

See merge request apipark/APIPark!78
2024-11-01 16:44:48 +08:00
maggieyyy e31d41a276 fix: Modify consumer list reload and subscriber option 2024-11-01 16:42:34 +08:00
Liujian 21d2abf716 Merge remote-tracking branch 'origin/main' into feature/permit 2024-11-01 16:19:35 +08:00
Liujian 3a86a88870 update subscribe permit 2024-11-01 16:19:25 +08:00
杨梦洁 6117a840e1 Merge branch 'feature/v1.2' into 'main'
fix: Add team selector to consumer

See merge request apipark/APIPark!77
2024-11-01 15:49:53 +08:00
maggieyyy 0e20987fb8 fix: Add team selector to consumer 2024-11-01 15:48:51 +08:00
Dot.L 65e7cab772 Update readme-zh-tw.md 2024-11-01 15:26:33 +08:00
Dot.L 935f2ac766 Update readme-zh-cn.md 2024-11-01 15:26:15 +08:00
Dot.L 3e12d7eb9c Update readme-jp.md 2024-11-01 15:25:55 +08:00
Dot.L d6095269b7 Update README.md 2024-11-01 15:25:15 +08:00
Liujian 0c415b4e32 Merge remote-tracking branch 'github-pro/main' into feature/permit 2024-11-01 14:59:54 +08:00
杨梦洁 bc1819b368 Merge branch 'feature/v1.2' into 'main'
Feature/v1.2

See merge request apipark/APIPark!76
2024-11-01 14:51:56 +08:00
Maggie a9be016c87 Merge pull request #127 from APIParkLab/feature/v1.2
Feature/v1.2
2024-11-01 14:49:21 +08:00
maggieyyy 11da5e9d26 fix: consumer menu access bug 2024-11-01 14:48:18 +08:00
maggieyyy 2c6c194821 fix: consumer menu access bug 2024-11-01 14:48:10 +08:00
杨梦洁 aedd8b4cc6 Merge branch 'feature/v1.2' into 'main'
fix: consumer menu access bug

See merge request apipark/APIPark!75
2024-11-01 14:46:49 +08:00
maggieyyy fa2607e9b8 fix: consumer menu access bug 2024-11-01 14:45:32 +08:00
Liujian 3783e5ee5d Merge remote-tracking branch 'github-pro/main' into feature/permit 2024-11-01 14:26:37 +08:00
Maggie fd56b8ffed Merge pull request #126 from APIParkLab/feature/v1.2
Feature/v1.2
2024-11-01 14:24:51 +08:00
杨梦洁 07e288be16 Merge branch 'feature/v1.2' into 'main'
Feature/v1.2

See merge request apipark/APIPark!74
2024-11-01 14:24:14 +08:00
maggieyyy 46143c3fe0 fix: Modify department children type 2024-11-01 14:23:05 +08:00
maggieyyy 83e3cc85f2 fix: Modify department children type 2024-11-01 14:21:04 +08:00
刘健 a4b50ae60a Merge branch 'feature/permit' into 'main'
Feature/permit

See merge request apipark/APIPark!73
2024-11-01 14:19:48 +08:00
Liujian a30d0c37eb update permit bug 2024-11-01 14:19:17 +08:00
maggieyyy 64fdf59905 fix: Add empty to member transfer 2024-11-01 13:54:33 +08:00
maggieyyy 1aa3f2fb05 fix: plugin config 2024-11-01 13:39:52 +08:00
Liujian 68e8cb72d3 Merge remote-tracking branch 'origin/main' into feature/permit 2024-11-01 11:03:21 +08:00
Liujian a86d3cd65a update permit 2024-11-01 11:01:45 +08:00
杨梦洁 a4b4cbf60f Merge branch 'feature/v1.2' into 'main'
fix: Modify subscribe options

See merge request apipark/APIPark!72
2024-10-31 14:16:30 +08:00
maggieyyy 2dcb7ebd74 fix: Modify subscribe options 2024-10-31 14:14:44 +08:00
maggieyyy ad6b64ca74 fix: Modify config 2024-10-31 13:59:06 +08:00
maggieyyy d27a2b8cf3 Merge branch 'main' into feature/mf 2024-10-31 09:27:08 +08:00
杨梦洁 8fa1985feb Merge branch 'feature/v1.2' into 'main'
fix: delete log

See merge request apipark/APIPark!71
2024-10-31 09:25:28 +08:00
Maggie 451efb8d3e Merge pull request #124 from APIParkLab/feature/v1.2
fix: delete log
2024-10-31 09:25:15 +08:00
maggieyyy d6bf3139ff fix: delete log 2024-10-31 09:14:24 +08:00
Maggie 309b9ea937 Merge pull request #123 from APIParkLab/feature/v1.2
fix: Modify access
2024-10-30 18:40:10 +08:00
杨梦洁 554bff38c6 Merge branch 'feature/v1.2' into 'main'
fix: Modify access

See merge request apipark/APIPark!70
2024-10-30 18:39:59 +08:00
maggieyyy d5e6062ec9 fix: Modify access 2024-10-30 18:38:39 +08:00
maggieyyy bcb68d552f fix: Modify mock plugin config 2024-10-30 17:52:41 +08:00
maggieyyy 3de87723ae fix: Modify mock plugin config 2024-10-30 17:51:10 +08:00
maggieyyy 535d70ac5a merge 2024-10-30 14:24:16 +08:00
Maggie d588d43aa1 Merge pull request #122 from APIParkLab/feature/v1.2
fix: Modify local path
2024-10-30 10:59:54 +08:00
Liujian f455cecb54 update ap-account version 2024-10-30 10:45:58 +08:00
杨梦洁 b88c0a9305 Merge branch 'feature/v1.2' into 'main'
fix: Modify local path

See merge request apipark/APIPark!69
2024-10-30 09:08:35 +08:00
maggieyyy 9030cff8ba fix: Modify local path 2024-10-29 18:32:45 +08:00
杨梦洁 98f73f799a Merge branch 'feature/v1.2' into 'main'
Feature/v1.2

See merge request apipark/APIPark!68
2024-10-29 17:46:36 +08:00
JackLiu d2e428ada8 Merge pull request #121 from rolealiu/main
Update v1.2 translation
2024-10-29 17:29:21 +08:00
JackLiu 74156ec84c Merge branch 'APIParkLab:main' into main 2024-10-29 17:28:51 +08:00
HaoZhen Liu f9e6bc92d7 update v1.2 translation 2024-10-29 17:28:41 +08:00
Maggie 9884586cc9 Merge pull request #120 from APIParkLab/feature/v1.2
Feature/v1.2
2024-10-29 17:05:29 +08:00
杨梦洁 ac717a6efb Merge branch 'feature/v1.2' into 'main'
fix: Add psw to account

See merge request apipark/APIPark!67
2024-10-29 16:47:44 +08:00
maggieyyy 2b7c1ded15 fix: Add psw to account 2024-10-29 16:44:31 +08:00
杨梦洁 daac27712d Merge branch 'feature/v1.2' into 'main'
fix: Modify member transfer icon and department

See merge request apipark/APIPark!66
2024-10-29 15:13:44 +08:00
maggieyyy 1080f33282 fix: Modify member transfer icon and department 2024-10-29 15:12:43 +08:00
Dot.L cc5c0a0a89 Merge pull request #119 from APIParkLab/feature/permission
update i18n
2024-10-29 14:20:19 +08:00
Liujian 85971447e1 update i18n 2024-10-29 14:19:36 +08:00
杨梦洁 256e8ef275 Merge branch 'feature/v1.2' into 'main'
fix: update translation

See merge request apipark/APIPark!65
2024-10-29 13:54:49 +08:00
Maggie c07e54ec03 Merge pull request #118 from APIParkLab/feature/v1.2
fix: update translation
2024-10-29 13:54:42 +08:00
maggieyyy 70f834e1cf fix: update translation 2024-10-29 13:52:48 +08:00
Maggie c320c6f2a2 Merge pull request #117 from APIParkLab/feature/v1.2
Feature/v1.2
2024-10-29 13:39:34 +08:00
杨梦洁 256eb60df8 Merge branch 'feature/v1.2' into 'main'
Feature/v1.2

See merge request apipark/APIPark!64
2024-10-29 13:39:06 +08:00
maggieyyy 5f03973bf2 fix: Modify guide link 2024-10-29 13:37:38 +08:00
maggieyyy ab2a0d8ae2 fix: Modify member transfer 2024-10-29 11:58:09 +08:00
Maggie 7e48402591 Merge pull request #116 from APIParkLab/feature/v1.2
fix: account translation
2024-10-29 09:21:36 +08:00
杨梦洁 aa85f6fcd1 Merge branch 'feature/v1.2' into 'main'
fix: account translation

See merge request apipark/APIPark!62
2024-10-29 09:21:19 +08:00
maggieyyy cd441ccfe7 fix: account translation 2024-10-29 09:19:35 +08:00
Dot.L 47f6519006 Merge pull request #114 from APIParkLab/feature/permission
update remark
2024-10-28 18:41:17 +08:00
Maggie 9679376cb2 Merge pull request #113 from APIParkLab/feature/v1.2
fix: table fields
2024-10-28 18:40:48 +08:00
杨梦洁 efa97c3bbc Merge branch 'feature/v1.2' into 'main'
fix: table fields

See merge request apipark/APIPark!61
2024-10-28 18:40:09 +08:00
Liujian 087e598be0 update remark 2024-10-28 18:39:55 +08:00
Dot.L 31aa8243ee Merge pull request #112 from APIParkLab/feature/permission
Feature/permission
2024-10-28 18:34:19 +08:00
Liujian d3d05ef539 Merge remote-tracking branch 'github-pro/main' into feature/permission
# Conflicts:
#	resources/access/access.yaml
2024-10-28 18:33:42 +08:00
maggieyyy d6062ea4e7 fix: table fields 2024-10-28 18:32:20 +08:00
Liujian 7949748951 update permission 2024-10-28 18:27:37 +08:00
Liujian 94f7392060 update access 2024-10-28 16:48:18 +08:00
Maggie 0d737bad57 Merge pull request #111 from APIParkLab/feature/v1.2
Feature/v1.2
2024-10-28 16:22:58 +08:00
杨梦洁 1225db50c9 Merge branch 'feature/v1.2' into 'main'
fix: Modify access fields

See merge request apipark/APIPark!59
2024-10-28 16:17:09 +08:00
maggieyyy e5d85bb3df fix: Modify access fields 2024-10-28 16:15:38 +08:00
杨梦洁 0f09e5c236 Merge branch 'feature/v1.2' into 'main'
fix: cosumer list bug

See merge request apipark/APIPark!58
2024-10-28 16:01:48 +08:00
maggieyyy 0f0204b647 fix: cosumer list bug 2024-10-28 16:00:15 +08:00
杨梦洁 94421d2622 Merge branch 'feature/v1.2' into 'main'
fix: Consumer list bugs

See merge request apipark/APIPark!57
2024-10-28 15:45:11 +08:00
maggieyyy 44f0b70461 fix: Consumer list bugs 2024-10-28 15:44:10 +08:00
刘健 f58768237b Merge branch 'feature/translate' into 'main'
Feature/translate

See merge request apipark/APIPark!56
2024-10-28 15:15:00 +08:00
Dot.L 588cf839e3 Merge pull request #110 from APIParkLab/feature/translate
Feature/translate
2024-10-28 15:07:35 +08:00
杨梦洁 aa40b62e0a Merge branch 'feature/v1.2' into 'main'
Feature/v1.2

See merge request apipark/APIPark!55
2024-10-28 15:02:10 +08:00
Dot.L b0b9affbe7 Merge pull request #1 from Dot-Liu/feature/translate
Feature/translate
2024-10-28 10:01:46 +08:00
maggieyyy bf2aefe2da fix: route config 2024-10-22 14:05:12 +08:00
maggieyyy 540a31f237 fix: aiservice router 2024-10-22 10:20:34 +08:00
maggieyyy 0505045c81 fix: router config 2024-10-21 17:41:30 +08:00
maggieyyy 1a18d79d94 fix: Add MF 2024-10-21 14:41:49 +08:00
295 changed files with 12404 additions and 4646 deletions
+1 -1
View File
@@ -3,4 +3,4 @@
/config.yml
/build/
/apipark
/aoplatform
.gitlab-ci.yml
-98
View File
@@ -1,98 +0,0 @@
variables:
PATH: /opt/go-1.21/go/bin/:/opt/node/node/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
GOROOT: /opt/go-1.21/go
GOPROXY: https://goproxy.cn
VERSION: $CI_COMMIT_SHORT_SHA
APP: apipark
APP_PRE: ${APP}_${VERSION}
BUILD_DIR: ${APP}-build
DEPLOY_DESC: "DEV 环境"
VIEW_ADDR: http://172.18.166.219:8288
SAVE_DIR: /opt/${APP}
NODE_OPTIONS: --max_old_space_size=8192
stages:
- notice
- prefix
- build
- deploy
- webhook
feishu-informer: # 飞书回调
stage: notice
variables:
DIFF_URL: "$CI_MERGE_REQUEST_PROJECT_URL/-/merge_requests/$CI_MERGE_REQUEST_IID/diffs"
rules:
- if: $CI_PIPELINE_SOURCE=="merge_request_event" && $CI_COMMIT_BRANCH =~ "main"
script:
- echo "merge request"
- |
curl -X POST -H "Content-Type: application/json" \
-d "{\"msg_type\":\"text\",\"content\":{\"text\":\"项目:${CI_PROJECT_NAME}\\n提交人:${GITLAB_USER_NAME}\\n提交信息:${CI_MERGE_REQUEST_TITLE}\\n合并分支信息:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} -> ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}\\n差异性地址:${DIFF_URL}\\n请及时review代码\"}}" \
https://open.feishu.cn/open-apis/bot/v2/hook/1c334752-2874-41a1-8f1b-3060f2d46b6c
prebuild:
stage: prefix
rules:
- if: $CI_COMMIT_BRANCH == "main"
script:
- echo "prebuild"
- chmod +x ./scripts/prefix.sh
- ./scripts/prefix.sh
builder:
stage: build
rules:
- if: $CI_COMMIT_BRANCH == "main"
script:
- set -e
- |
if [ ! -d "../artifacts" ]; then
mkdir -p ../artifacts
fi
if [ -d "../artifacts/dist" ]; then
cp -r ../artifacts/dist frontend/dist
fi
- |
if [ -n "$(git diff --name-status HEAD~1 HEAD -- frontend)" ]; then
./scripts/build.sh $BUILD_DIR ${VERSION} all ""
else
./scripts/build.sh $BUILD_DIR ${VERSION}
fi
if [ -d "frontend/dist" ]; then
echo "copy frontend/dist to artifacts/dist"
rm -fr ../artifacts/dist
cp -r frontend/dist ../artifacts/dist
fi
cp $BUILD_DIR/${APP_PRE}_linux_amd64.tar.gz ${SAVE_DIR}
deployer:
stage: deploy
rules:
- if: $CI_COMMIT_BRANCH == "main"
variables:
APIPARK_GUEST_MODE: allow
APIPARK_GUEST_ID: dklejrfbhjqwdh
script:
- cd ${SAVE_DIR};mkdir -p ${APP_PRE};tar -zxvf ${APP_PRE}_linux_amd64.tar.gz -C ${APP_PRE};cd ${APP_PRE};./install.sh ${SAVE_DIR};./run.sh restart;cd ${SAVE_DIR} && ./clean.sh ${APP_PRE}
when: on_success
success:
stage: webhook
rules:
- if: $CI_COMMIT_BRANCH == "main"
script:
- |
curl -X POST -H "Content-Type: application/json" \
-d "{\"msg_type\":\"text\",\"content\":{\"text\":\"最近一次提交:${CI_COMMIT_TITLE}\\n提交人:${GITLAB_USER_NAME}\\n项目:${CI_PROJECT_NAME}\\n环境:${DEPLOY_DESC}\\n更新部署完成.\\n访问地址:${VIEW_ADDR}\\n工作流地址:${CI_PIPELINE_URL}\"}}" \
https://open.feishu.cn/open-apis/bot/v2/hook/c3672932-4dfa-4989-8023-0128bae59338
when: on_success
failure:
stage: webhook
rules:
- if: $CI_COMMIT_BRANCH == "main"
script:
- |
curl -X POST -H "Content-Type: application/json" \
-d "{\"msg_type\":\"text\",\"content\":{\"text\":\"最近一次提交:${CI_COMMIT_TITLE}\\n提交人:${GITLAB_USER_NAME}\\n项目:${CI_PROJECT_NAME}\\n环境:${DEPLOY_DESC}\\n更新部署失败,请及时到gitlab上查看\\n工作流地址:${CI_PIPELINE_URL}\"}}" \
https://open.feishu.cn/open-apis/bot/v2/hook/c3672932-4dfa-4989-8023-0128bae59338
when: on_failure
+9
View File
@@ -0,0 +1,9 @@
{
"cSpell.words": [
"Antd",
"apinto",
"Apipark",
"logsettings",
"resourcesettings"
]
}
+1 -1
View File
@@ -197,7 +197,7 @@ To achieve this goal, we plan to add new features to APIPark, including:
<br>
# 📕 Documentation
Visit [APIPark Documentation](https://docs.apipark.com/docs/install) for detailed installation guides, API references, and usage instructions.
Visit [APIPark Documentation](https://docs.apipark.com/docs/deploy) for detailed installation guides, API references, and usage instructions.
<br>
+6 -6
View File
@@ -27,7 +27,7 @@ type imlAPIController struct {
}
func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_api_dto.CreateAPI) (*ai_api_dto.API, error) {
info, err := i.serviceModule.Get(ctx, serviceId)
_, err := i.serviceModule.Get(ctx, serviceId)
if err != nil {
return nil, err
}
@@ -52,7 +52,7 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_
plugins["ai_formatter"] = api.PluginSetting{
Config: plugin_model.ConfigType{
"model": input.AiModel.Id,
"provider": fmt.Sprintf("%s@ai-provider", info.Provider.Id),
"provider": fmt.Sprintf("%s@ai-provider", input.AiModel.Provider),
"config": input.AiModel.Config,
},
}
@@ -73,7 +73,7 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_
Retry: input.Retry,
Plugins: plugins,
},
Upstream: info.Provider.Id,
Upstream: input.AiModel.Provider,
Disable: false,
})
@@ -86,7 +86,7 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_
}
func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string, input *ai_api_dto.EditAPI) (*ai_api_dto.API, error) {
info, err := i.serviceModule.Get(ctx, serviceId)
_, err := i.serviceModule.Get(ctx, serviceId)
if err != nil {
return nil, err
}
@@ -106,11 +106,11 @@ func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string
proxy.Plugins["ai_formatter"] = api.PluginSetting{
Config: plugin_model.ConfigType{
"model": input.AiModel.Id,
"provider": fmt.Sprintf("%s@ai-provider", info.Provider.Id),
"provider": fmt.Sprintf("%s@ai-provider", input.AiModel.Provider),
"config": input.AiModel.Config,
},
}
upstream = &info.Provider.Id
upstream = &input.AiModel.Provider
}
if input.AiPrompt != nil {
+2 -2
View File
@@ -45,8 +45,8 @@ func (p *imlCluster) Check(ctx *gin.Context, input *cluster_dto.CheckCluster) ([
// return id, nil
//}
//
//func (p *imlCluster) Search(ctx *gin.Context, keyword string) ([]*parition_dto.Item, error) {
// return p.module.Search(ctx, keyword)
//func (p *imlCluster) SearchByDriver(ctx *gin.Context, keyword string) ([]*parition_dto.Item, error) {
// return p.module.SearchByDriver(ctx, keyword)
//}
//
//func (p *imlCluster) Simple(ctx *gin.Context) ([]*parition_dto.Simple, error) {
+21
View File
@@ -0,0 +1,21 @@
package log
import (
"reflect"
log_dto "github.com/APIParkLab/APIPark/module/log/dto"
"github.com/eolinker/go-common/autowire"
"github.com/gin-gonic/gin"
)
type ILogController interface {
Save(ctx *gin.Context, driver string, input *log_dto.Save) error
Get(ctx *gin.Context, driver string) (*log_dto.LogSource, error)
}
func init() {
logController := &imlLogController{}
autowire.Auto[ILogController](func() reflect.Value {
return reflect.ValueOf(logController)
})
}
+19
View File
@@ -0,0 +1,19 @@
package log
import (
"github.com/APIParkLab/APIPark/module/log"
log_dto "github.com/APIParkLab/APIPark/module/log/dto"
"github.com/gin-gonic/gin"
)
type imlLogController struct {
module log.ILogModule `autowired:""`
}
func (c *imlLogController) Save(ctx *gin.Context, driver string, input *log_dto.Save) error {
return c.module.Save(ctx, driver, input)
}
func (c *imlLogController) Get(ctx *gin.Context, driver string) (*log_dto.LogSource, error) {
return c.module.Get(ctx, driver)
}
+64
View File
@@ -17,6 +17,70 @@ type imlMonitorStatisticController struct {
module monitor.IMonitorStatisticModule `autowired:""`
}
func (i *imlMonitorStatisticController) Statistics(ctx *gin.Context, dataType string, input *monitor_dto.StatisticInput) (interface{}, error) {
switch dataType {
case monitor_dto.DataTypeApi:
return i.module.ApiStatistics(ctx, input)
case monitor_dto.DataTypeProvider:
return i.module.ProviderStatistics(ctx, input)
case monitor_dto.DataTypeSubscriber:
return i.module.SubscriberStatistics(ctx, input)
default:
return nil, fmt.Errorf("unsupported data type: %s", dataType)
}
}
func (i *imlMonitorStatisticController) InvokeTrend(ctx *gin.Context, dataType string, id string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) {
switch dataType {
case monitor_dto.DataTypeApi:
return i.module.APITrend(ctx, id, input)
case monitor_dto.DataTypeProvider:
return i.module.ProviderTrend(ctx, id, input)
case monitor_dto.DataTypeSubscriber:
return i.module.SubscriberTrend(ctx, id, input)
default:
return nil, "", fmt.Errorf("unsupported data type: %s", dataType)
}
}
func (i *imlMonitorStatisticController) InvokeTrendInner(ctx *gin.Context, dataType string, typ string, api string, provider string, subscriber string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error) {
if dataType == monitor_dto.DataTypeApi && typ == monitor_dto.DataTypeSubscriber || dataType == monitor_dto.DataTypeSubscriber && typ == monitor_dto.DataTypeApi {
return i.module.InvokeTrendWithSubscriberAndApi(ctx, api, subscriber, input)
} else if dataType == monitor_dto.DataTypeApi && typ == monitor_dto.DataTypeProvider || dataType == monitor_dto.DataTypeProvider && typ == monitor_dto.DataTypeApi {
return i.module.InvokeTrendWithProviderAndApi(ctx, provider, api, input)
}
return nil, "", fmt.Errorf("unsupported detail type: %s, data type is %s", typ, dataType)
}
func (i *imlMonitorStatisticController) StatisticsInner(ctx *gin.Context, dataType string, typ string, id string, input *monitor_dto.StatisticInput) (interface{}, error) {
switch dataType {
case monitor_dto.DataTypeApi:
switch typ {
case monitor_dto.DataTypeProvider:
return i.module.ProviderStatisticsOnApi(ctx, id, input)
case monitor_dto.DataTypeSubscriber:
return i.module.SubscriberStatisticsOnApi(ctx, id, input)
default:
return nil, fmt.Errorf("unsupported detail type: %s, data type is %s", typ, dataType)
}
case monitor_dto.DataTypeProvider:
switch typ {
case monitor_dto.DataTypeApi:
return i.module.ApiStatisticsOnProvider(ctx, id, input)
default:
return nil, fmt.Errorf("unsupported detail type: %s, data type is %s", typ, dataType)
}
case monitor_dto.DataTypeSubscriber:
switch typ {
case monitor_dto.DataTypeApi:
return i.module.ApiStatisticsOnSubscriber(ctx, id, input)
default:
return nil, fmt.Errorf("unsupported detail type: %s, data type is %s", typ, dataType)
}
}
return nil, fmt.Errorf("unsupported data type: %s", dataType)
}
func (i *imlMonitorStatisticController) OverviewMessageTrend(ctx *gin.Context, input *monitor_dto.CommonInput) ([]time.Time, []float64, []float64, string, error) {
trend, timeInterval, err := i.module.MessageTrend(ctx, input)
if err != nil {
+6 -1
View File
@@ -16,7 +16,12 @@ type IMonitorStatisticController interface {
OverviewInvokeTrend(ctx *gin.Context, input *monitor_dto.CommonInput) ([]time.Time, []int64, []int64, []int64, []int64, []float64, []float64, string, error)
OverviewMessageTrend(ctx *gin.Context, input *monitor_dto.CommonInput) ([]time.Time, []float64, []float64, string, error)
//Statistics(ctx *gin.Context, dataType string, input *monitor_dto.StatisticInput) (interface{}, error)
Statistics(ctx *gin.Context, dataType string, input *monitor_dto.StatisticInput) (interface{}, error)
InvokeTrend(ctx *gin.Context, dataType string, id string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error)
InvokeTrendInner(ctx *gin.Context, dataType string, typ string, api string, provider string, subscriber string, input *monitor_dto.CommonInput) (*monitor_dto.MonInvokeCountTrend, string, error)
StatisticsInner(ctx *gin.Context, dataType string, typ string, id string, input *monitor_dto.StatisticInput) (interface{}, error)
}
type IMonitorConfigController interface {
+6 -1
View File
@@ -1,12 +1,13 @@
package router
import (
"io"
api_doc "github.com/APIParkLab/APIPark/module/api-doc"
api_doc_dto "github.com/APIParkLab/APIPark/module/api-doc/dto"
"github.com/APIParkLab/APIPark/module/router"
router_dto "github.com/APIParkLab/APIPark/module/router/dto"
"github.com/gin-gonic/gin"
"io"
)
var _ IRouterController = (*imlAPIController)(nil)
@@ -15,6 +16,10 @@ type imlAPIController struct {
module router.IRouterModule `autowired:""`
}
func (i *imlAPIController) Simple(ctx *gin.Context, input *router_dto.InputSimpleAPI) ([]*router_dto.SimpleItem, error) {
return i.module.SimpleAPIs(ctx, input)
}
func (i *imlAPIController) Detail(ctx *gin.Context, serviceId string, apiId string) (*router_dto.Detail, error) {
return i.module.Detail(ctx, serviceId, apiId)
}
+3 -1
View File
@@ -1,9 +1,10 @@
package router
import (
api_doc_dto "github.com/APIParkLab/APIPark/module/api-doc/dto"
"reflect"
api_doc_dto "github.com/APIParkLab/APIPark/module/api-doc/dto"
"github.com/gin-gonic/gin"
"github.com/eolinker/go-common/autowire"
@@ -24,6 +25,7 @@ type IRouterController interface {
Delete(ctx *gin.Context, serviceId string, apiId string) error
// Prefix 获取API前缀
Prefix(ctx *gin.Context, serviceId string) (string, bool, error)
Simple(ctx *gin.Context, input *router_dto.InputSimpleAPI) ([]*router_dto.SimpleItem, error)
}
type IAPIDocController interface {
+159 -31
View File
@@ -5,6 +5,19 @@ import (
"fmt"
"net/http"
"strings"
"time"
"github.com/eolinker/go-common/pm3"
"github.com/APIParkLab/APIPark/module/system"
"github.com/getkin/kin-openapi/openapi3"
api_doc "github.com/APIParkLab/APIPark/module/api-doc"
upstream_dto "github.com/APIParkLab/APIPark/module/upstream/dto"
"github.com/eolinker/eosc/log"
application_authorization "github.com/APIParkLab/APIPark/module/application-authorization"
application_authorization_dto "github.com/APIParkLab/APIPark/module/application-authorization/dto"
@@ -22,7 +35,6 @@ import (
"github.com/APIParkLab/APIPark/module/service"
service_dto "github.com/APIParkLab/APIPark/module/service/dto"
"github.com/APIParkLab/APIPark/module/upstream"
upstream_dto "github.com/APIParkLab/APIPark/module/upstream/dto"
"github.com/eolinker/go-common/store"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
@@ -39,29 +51,102 @@ type imlServiceController struct {
docModule service.IServiceDocModule `autowired:""`
aiAPIModule ai_api.IAPIModule `autowired:""`
routerModule router.IRouterModule `autowired:""`
apiDocModule api_doc.IAPIDocModule `autowired:""`
providerModule ai.IProviderModule `autowired:""`
upstreamModule upstream.IUpstreamModule `autowired:""`
settingModule system.ISettingModule `autowired:""`
transaction store.ITransaction `autowired:""`
}
func newAIUpstream(id string, provider string, uri model_runtime.IProviderURI) *upstream_dto.Upstream {
return &upstream_dto.Upstream{
Type: "http",
Balance: "round-robin",
Timeout: 300000,
Retry: 0,
Remark: fmt.Sprintf("auto create by ai service %s,provider is %s", id, provider),
LimitPeerSecond: 0,
ProxyHeaders: nil,
Scheme: uri.Scheme(),
PassHost: "node",
Nodes: []*upstream_dto.NodeConfig{
{
Address: uri.Host(),
Weight: 100,
},
},
var (
loader = openapi3.NewLoader()
)
func (i *imlServiceController) swagger(ctx *gin.Context, id string) (*openapi3.T, error) {
doc, err := i.apiDocModule.GetDoc(ctx, id)
if err != nil {
return nil, err
}
tmp, err := loader.LoadFromData([]byte(doc.Content))
if err != nil {
return nil, err
}
cfg := i.settingModule.Get(ctx)
tmp.AddServer(&openapi3.Server{
URL: cfg.InvokeAddress,
})
return tmp, nil
}
func (i *imlServiceController) ExportSwagger(ctx *gin.Context) {
id, has := ctx.Params.Get("id")
if !has {
ctx.JSON(200, &pm3.Response{
Code: -1,
Success: "fail",
Message: fmt.Sprintf("id is required"),
})
return
}
s, err := i.module.Get(ctx, id)
if err != nil {
ctx.JSON(200, &pm3.Response{
Code: -1,
Success: "fail",
Message: err.Error(),
})
return
}
tmp, err := i.swagger(ctx, id)
if err != nil {
ctx.JSON(200, &pm3.Response{
Code: -1,
Success: "fail",
Message: err.Error(),
})
return
}
data, _ := tmp.MarshalJSON()
ctx.Status(200)
// 设置响应头
ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s.json", strings.Replace(s.Name, " ", "_", -1)))
ctx.Header("Content-Type", "application/octet-stream")
ctx.Header("Content-Transfer-Encoding", "binary")
ctx.Writer.Write(data)
return
}
func (i *imlServiceController) Swagger(ctx *gin.Context) {
id, has := ctx.Params.Get("id")
if !has {
ctx.JSON(200, &pm3.Response{
Code: -1,
Success: "fail",
Message: fmt.Sprintf("id is required"),
})
return
}
tmp, err := i.swagger(ctx, id)
if err != nil {
ctx.JSON(200, &pm3.Response{
Code: -1,
Success: "fail",
Message: err.Error(),
})
return
}
ctx.JSON(200, tmp)
return
}
func (i *imlServiceController) Simple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error) {
return i.module.Simple(ctx)
}
func (i *imlServiceController) MySimple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error) {
return i.module.MySimple(ctx)
}
func (i *imlServiceController) editAIService(ctx *gin.Context, id string, input *service_dto.EditService) (*service_dto.Service, error) {
@@ -212,10 +297,7 @@ func (i *imlServiceController) createAIService(ctx *gin.Context, teamID string,
if err != nil {
return err
}
//_, err = i.upstreamModule.Save(ctx, info.Id, newAIUpstream(info.Id, *input.Provider, p.URI()))
//if err != nil {
// return err
//}
return i.docModule.SaveServiceDoc(ctx, info.Id, &service_dto.SaveServiceDoc{
Doc: "The Translation API allows developers to translate text from one language to another. It supports multiple languages and enables easy integration of high-quality translation features into applications. With simple API requests, you can quickly translate content into different target languages.",
})
@@ -228,15 +310,11 @@ func (i *imlServiceController) SearchMyServices(ctx *gin.Context, teamId string,
return i.module.SearchMyServices(ctx, teamId, keyword)
}
//func (i *imlServiceController) Simple(ctx *gin.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) {
// return i.module.Simple(ctx, keyword)
//}
//
//func (i *imlServiceController) MySimple(ctx *gin.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) {
// return i.module.MySimple(ctx, keyword)
//}
func (i *imlServiceController) Get(ctx *gin.Context, id string) (*service_dto.Service, error) {
now := time.Now()
defer func() {
log.Infof("get service %s cost %d ms", id, time.Since(now).Milliseconds())
}()
return i.module.Get(ctx, id)
}
@@ -248,7 +326,33 @@ func (i *imlServiceController) Create(ctx *gin.Context, teamID string, input *se
if input.Kind == "ai" {
return i.createAIService(ctx, teamID, input)
}
return i.module.Create(ctx, teamID, input)
var err error
var info *service_dto.Service
err = i.transaction.Transaction(ctx, func(txCtx context.Context) error {
info, err = i.module.Create(txCtx, teamID, input)
if err != nil {
return err
}
path := fmt.Sprintf("/%s/", strings.Trim(input.Prefix, "/"))
_, err = i.routerModule.Create(txCtx, info.Id, &router_dto.Create{
Id: uuid.New().String(),
Name: "",
Path: path + "*",
Methods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodPatch, http.MethodOptions},
Description: "auto create by create service",
Protocols: []string{"http", "https"},
MatchRules: nil,
Upstream: "",
Proxy: &router_dto.InputProxy{
Path: path,
Timeout: 30000,
Retry: 0,
},
Disable: false,
})
return err
})
return info, err
}
func (i *imlServiceController) Edit(ctx *gin.Context, id string, input *service_dto.EditService) (*service_dto.Service, error) {
@@ -279,6 +383,10 @@ type imlAppController struct {
authModule application_authorization.IAuthorizationModule `autowired:""`
}
func (i *imlAppController) SearchCanSubscribe(ctx *gin.Context, serviceId string) ([]*service_dto.SimpleAppItem, error) {
return i.module.SearchCanSubscribe(ctx, serviceId)
}
func (i *imlAppController) Search(ctx *gin.Context, teamId string, keyword string) ([]*service_dto.AppItem, error) {
return i.module.Search(ctx, teamId, keyword)
}
@@ -327,3 +435,23 @@ func (i *imlAppController) GetApp(ctx *gin.Context, appId string) (*service_dto.
func (i *imlAppController) DeleteApp(ctx *gin.Context, appId string) error {
return i.module.DeleteApp(ctx, appId)
}
func newAIUpstream(id string, provider string, uri model_runtime.IProviderURI) *upstream_dto.Upstream {
return &upstream_dto.Upstream{
Type: "http",
Balance: "round-robin",
Timeout: 300000,
Retry: 0,
Remark: fmt.Sprintf("auto create by ai service %s,provider is %s", id, provider),
LimitPeerSecond: 0,
ProxyHeaders: nil,
Scheme: uri.Scheme(),
PassHost: "node",
Nodes: []*upstream_dto.NodeConfig{
{
Address: uri.Host(),
Weight: 100,
},
},
}
}
+5 -5
View File
@@ -24,12 +24,11 @@ type IServiceController interface {
Delete(ctx *gin.Context, id string) error
ServiceDoc(ctx *gin.Context, id string) (*service_dto.ServiceDoc, error)
SaveServiceDoc(ctx *gin.Context, id string, input *service_dto.SaveServiceDoc) error
Simple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error)
MySimple(ctx *gin.Context) ([]*service_dto.SimpleServiceItem, error)
//createAIService(ctx *gin.Context, teamID string, input *service_dto.CreateService) (*service_dto.Service, error)
//editAIService(ctx *gin.Context, id string, input *service_dto.EditService) (*service_dto.Service, error)
//DeleteAIService(ctx *gin.Context, id string) error
//SearchMyAIServices(ctx *gin.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error)
//SearchAIServices(ctx *gin.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error)
Swagger(ctx *gin.Context)
ExportSwagger(ctx *gin.Context)
}
type IAppController interface {
@@ -42,6 +41,7 @@ type IAppController interface {
// SimpleApps 获取简易项目列表
SimpleApps(ctx *gin.Context, keyword string) ([]*service_dto.SimpleAppItem, error)
MySimpleApps(ctx *gin.Context, keyword string) ([]*service_dto.SimpleAppItem, error)
SearchCanSubscribe(ctx *gin.Context, keyword string) ([]*service_dto.SimpleAppItem, error)
GetApp(ctx *gin.Context, appId string) (*service_dto.App, error)
DeleteApp(ctx *gin.Context, appId string) error
}
+263
View File
@@ -0,0 +1,263 @@
package strategy
import (
"encoding/base64"
"encoding/json"
"fmt"
"sort"
"strconv"
"time"
"github.com/eolinker/go-common/utils"
strategy_filter "github.com/APIParkLab/APIPark/strategy-filter"
"github.com/APIParkLab/APIPark/module/service"
"github.com/APIParkLab/APIPark/module/strategy"
strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto"
"github.com/gin-gonic/gin"
)
var _ IStrategyController = (*imlStrategyController)(nil)
type imlStrategyController struct {
strategyModule strategy.IStrategyModule `autowired:""`
serviceModule service.IServiceModule `autowired:""`
}
func (i *imlStrategyController) Restore(ctx *gin.Context, id string) error {
return i.strategyModule.Restore(ctx, id)
}
func (i *imlStrategyController) DeleteServiceStrategy(ctx *gin.Context, serviceId string, id string) error {
return i.strategyModule.DeleteServiceStrategy(ctx, serviceId, id)
}
func (i *imlStrategyController) ToPublish(ctx *gin.Context, driver string) ([]*strategy_dto.ToPublishItem, string, string, bool, error) {
list, err := i.strategyModule.ToPublish(ctx, driver)
if err != nil {
return nil, "", "", false, err
}
data, _ := json.Marshal(list)
source := base64.StdEncoding.EncodeToString(data)
return list, source, time.Now().Format("20060102150405") + "-release", len(list) > 0, nil
}
func (i *imlStrategyController) FilterGlobalRemote(ctx *gin.Context, name string) ([]*strategy_dto.Title, []any, int64, string, string, error) {
f, has := strategy_filter.RemoteFilter(name)
if !has {
return nil, nil, 0, "", "", fmt.Errorf("filter not found: %s", name)
}
scopeAllow := false
for _, s := range f.Scopes() {
if s == strategy_filter.ScopeGlobal {
scopeAllow = true
break
}
}
if !scopeAllow {
return nil, nil, 0, "", "", fmt.Errorf("scope not allowed: %s", name)
}
list, total, err := f.RemoteList(ctx, "", nil, -1, -1)
if err != nil {
return nil, nil, 0, "", "", err
}
return utils.SliceToSlice(f.Titles(), func(l strategy_filter.OptionTitle) *strategy_dto.Title {
return &strategy_dto.Title{
Field: l.Field,
Title: l.Title,
}
}), list, total, f.Key(), f.Key(), nil
}
func (i *imlStrategyController) FilterServiceRemote(ctx *gin.Context, serviceId string, name string) ([]*strategy_dto.Title, []any, int64, string, string, error) {
f, has := strategy_filter.RemoteFilter(name)
if !has {
return nil, nil, 0, "", "", fmt.Errorf("filter not found: %s", name)
}
scopeAllow := false
for _, s := range f.Scopes() {
if s == strategy_filter.ScopeService {
scopeAllow = true
break
}
}
if !scopeAllow {
return nil, nil, 0, "", "", fmt.Errorf("scope not allowed: %s", name)
}
list, total, err := f.RemoteList(ctx, "", map[string]interface{}{"service": serviceId}, -1, -1)
if err != nil {
return nil, nil, 0, "", "", err
}
return utils.SliceToSlice(f.Titles(), func(l strategy_filter.OptionTitle) *strategy_dto.Title {
return &strategy_dto.Title{
Field: l.Field,
Title: l.Title,
}
}), list, total, f.Key(), "list", nil
}
func (i *imlStrategyController) filterOptions(ctx *gin.Context, scope string) ([]*strategy_dto.FilterOption, error) {
m, has := strategy_filter.Options(scope)
if !has {
return nil, fmt.Errorf("scope not found: %s", scope)
}
list := utils.MapToSlice(m, func(key string, value *strategy_filter.Option) *strategy_dto.FilterOption {
pattern := ""
if value.Pattern != nil {
pattern = value.Pattern.String()
}
return &strategy_dto.FilterOption{
Name: value.Name,
Title: value.Title,
Type: value.Type,
Pattern: pattern,
Options: value.Options,
}
})
sort.Slice(list, func(i, j int) bool {
return list[i].Name < list[j].Name
})
return list, nil
}
func (i *imlStrategyController) FilterServiceOptions(ctx *gin.Context) ([]*strategy_dto.FilterOption, error) {
return i.filterOptions(ctx, strategy_filter.ScopeService)
}
func (i *imlStrategyController) FilterGlobalOptions(ctx *gin.Context) ([]*strategy_dto.FilterOption, error) {
return i.filterOptions(ctx, strategy_filter.ScopeGlobal)
}
func (i *imlStrategyController) GetStrategy(ctx *gin.Context, id string) (*strategy_dto.Strategy, error) {
return i.strategyModule.Get(ctx, id)
}
func (i *imlStrategyController) search(ctx *gin.Context, keyword string, scope strategy_dto.Scope, target string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) {
p, err := strconv.Atoi(page)
if err != nil {
if page != "" {
return nil, 0, fmt.Errorf("page error: %s", err)
}
p = 1
}
ps, err := strconv.Atoi(pageSize)
if err != nil {
if pageSize != "" {
return nil, 0, fmt.Errorf("page size error: %s", err)
}
ps = 20
}
ss := make([]string, 0)
json.Unmarshal([]byte(sort), &ss)
fs := make([]string, 0)
json.Unmarshal([]byte(filters), &fs)
list, total, err := i.strategyModule.Search(ctx, keyword, driver, scope, target, p, ps, fs, ss...)
if err != nil {
return nil, 0, err
}
return list, total, nil
}
func (i *imlStrategyController) GlobalStrategyList(ctx *gin.Context, keyword string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) {
return i.search(ctx, keyword, strategy_dto.ToScope(strategy_dto.ScopeGlobal), "", driver, page, pageSize, order, sort, filters)
}
func (i *imlStrategyController) CreateGlobalStrategy(ctx *gin.Context, driver string, input *strategy_dto.Create) error {
input.Driver = driver
input.Scope = strategy_dto.ToScope(strategy_dto.ScopeGlobal)
return i.strategyModule.Create(ctx, input)
}
func (i *imlStrategyController) PublishGlobalStrategy(ctx *gin.Context, driver string) error {
return i.strategyModule.Publish(ctx, driver, strategy_dto.ScopeGlobal, "")
}
func (i *imlStrategyController) ServiceStrategyList(ctx *gin.Context, keyword string, serviceId string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error) {
return i.search(ctx, keyword, strategy_dto.ToScope(strategy_dto.ScopeService), serviceId, driver, page, pageSize, order, sort, filters)
}
func (i *imlStrategyController) CreateServiceStrategy(ctx *gin.Context, serviceId string, driver string, input *strategy_dto.Create) error {
_, err := i.serviceModule.Get(ctx, serviceId)
if err != nil {
return fmt.Errorf("create service strategy error: %s", err)
}
input.Driver = driver
input.Scope = strategy_dto.ToScope(strategy_dto.ScopeService)
input.Target = serviceId
return i.strategyModule.Create(ctx, input)
}
func (i *imlStrategyController) EditStrategy(ctx *gin.Context, id string, input *strategy_dto.Edit) error {
return i.strategyModule.Edit(ctx, id, input)
}
func (i *imlStrategyController) EnableStrategy(ctx *gin.Context, id string) error {
return i.strategyModule.Enable(ctx, id)
}
func (i *imlStrategyController) DisableStrategy(ctx *gin.Context, id string) error {
return i.strategyModule.Disable(ctx, id)
}
func (i *imlStrategyController) DeleteStrategy(ctx *gin.Context, id string) error {
return i.strategyModule.Delete(ctx, id)
}
func genTime(t string, defaultValue time.Time) (time.Time, error) {
if t == "" {
return defaultValue, nil
}
s, err := strconv.ParseInt(t, 10, 64)
if err != nil {
return time.Time{}, err
}
return time.Unix(s, 0), nil
}
func (i *imlStrategyController) GetStrategyLogs(ctx *gin.Context, keyword string, strategyId string, start string, end string, limit string, offset string) ([]*strategy_dto.LogItem, int64, error) {
now := time.Now()
s, err := genTime(start, now.Add(-time.Hour*24*30))
if err != nil {
return nil, 0, fmt.Errorf("start time error: %s", err)
}
e, err := genTime(end, now)
if err != nil {
return nil, 0, fmt.Errorf("end time error: %s", err)
}
if s.After(e) {
return nil, 0, fmt.Errorf("start time must be less than end time")
}
l, err := strconv.ParseInt(limit, 10, 64)
if err != nil && limit != "" {
return nil, 0, err
}
o, err := strconv.ParseInt(offset, 10, 64)
if err != nil && offset != "" {
return nil, 0, err
}
if l < 1 {
l = 15
}
if o < 1 {
o = 1
}
return i.strategyModule.GetStrategyLogs(ctx, keyword, strategyId, s, e, l, o)
}
func (i *imlStrategyController) LogInfo(ctx *gin.Context, id string) (*strategy_dto.LogInfo, error) {
return i.strategyModule.StrategyLogInfo(ctx, id)
}
+48
View File
@@ -0,0 +1,48 @@
package strategy
import (
"reflect"
strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto"
"github.com/eolinker/go-common/autowire"
"github.com/gin-gonic/gin"
)
type IStrategyController interface {
GlobalStrategyList(ctx *gin.Context, keyword string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error)
CreateGlobalStrategy(ctx *gin.Context, driver string, input *strategy_dto.Create) error
PublishGlobalStrategy(ctx *gin.Context, driver string) error
ServiceStrategyList(ctx *gin.Context, keyword string, serviceId string, driver string, page string, pageSize string, order string, sort string, filters string) ([]*strategy_dto.StrategyItem, int64, error)
CreateServiceStrategy(ctx *gin.Context, serviceId string, driver string, input *strategy_dto.Create) error
EditStrategy(ctx *gin.Context, id string, input *strategy_dto.Edit) error
GetStrategy(ctx *gin.Context, id string) (*strategy_dto.Strategy, error)
EnableStrategy(ctx *gin.Context, id string) error
DisableStrategy(ctx *gin.Context, id string) error
DeleteStrategy(ctx *gin.Context, id string) error
DeleteServiceStrategy(ctx *gin.Context, serviceId string, id string) error
Restore(ctx *gin.Context, id string) error
FilterGlobalOptions(ctx *gin.Context) ([]*strategy_dto.FilterOption, error)
FilterServiceOptions(ctx *gin.Context) ([]*strategy_dto.FilterOption, error)
FilterGlobalRemote(ctx *gin.Context, name string) ([]*strategy_dto.Title, []any, int64, string, string, error)
FilterServiceRemote(ctx *gin.Context, serviceId string, name string) ([]*strategy_dto.Title, []any, int64, string, string, error)
ToPublish(ctx *gin.Context, driver string) ([]*strategy_dto.ToPublishItem, string, string, bool, error)
GetStrategyLogs(ctx *gin.Context, keyword string, strategyId string, start string, end string, limit string, offset string) ([]*strategy_dto.LogItem, int64, error)
LogInfo(ctx *gin.Context, id string) (*strategy_dto.LogInfo, error)
}
type IStrategyCommonController interface {
}
func init() {
autowire.Auto[IStrategyController](func() reflect.Value {
return reflect.ValueOf(&imlStrategyController{})
})
}
+22 -1
View File
@@ -265,7 +265,8 @@ func (i *imlInitController) OnInit() {
return fmt.Errorf("create default team error: %v", err)
}
// 创建Rest服务
_, err = i.serviceModule.Create(ctx, info.Id, &service_dto.CreateService{
restPath := "/rest-demo"
serviceInfo, err := i.serviceModule.Create(ctx, info.Id, &service_dto.CreateService{
Name: "REST Demo Service",
Prefix: "/rest-demo",
Description: "Auto created By APIPark",
@@ -277,6 +278,26 @@ func (i *imlInitController) OnInit() {
if err != nil {
return fmt.Errorf("create default service error: %v", err)
}
path := fmt.Sprintf("/%s/", strings.Trim(restPath, "/"))
_, err = i.routerModule.Create(ctx, serviceInfo.Id, &router_dto.Create{
Id: uuid.NewString(),
Name: "",
Path: path + "*",
Methods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodPatch, http.MethodOptions},
Description: "auto create by create service",
Protocols: []string{"http", "https"},
MatchRules: nil,
Upstream: "",
Proxy: &router_dto.InputProxy{
Path: path,
Timeout: 30000,
Retry: 0,
},
Disable: false,
})
if err != nil {
return fmt.Errorf("create default router error: %v", err)
}
// 创建AI服务
err = i.createAIService(ctx, info.Id, &service_dto.CreateService{
Name: "AI Demo Service",
-17
View File
@@ -1,17 +0,0 @@
# 部署
## 代码同步
packages目录下,部分子项目为企业版独有,不要同步到开源版:
packages/businessEntry, packages/openApi, packages/systemRunning, README.pro.md
## 安装依赖
建议使用pnpm
`npm install -g pnpm`
使用pnpm安装依赖
`pnpm install`
## 编译
### 开源版本
`pnpm run build`
### 企业版本
`pnpm run build:pro`
-2
View File
@@ -9,11 +9,9 @@
"scripts": {
"test": "jest",
"build": "set NODE_OPTIONS=--max-old-space-size=4096 && lerna run build --scope=core --stream --verbose ",
"build:pro": "set NODE_OPTIONS=--max-old-space-size=4096 && lerna run build --scope=business-entry --stream --verbose ",
"serve": "lerna run preview --parallel",
"serve:remotes": "lerna run serve --scope=remote --parallel",
"dev": "lerna run dev --scope=core --stream",
"dev:pro": "lerna run dev --scope=business-entry --stream",
"stop": "kill-port --port 5000",
"scan": "i18next-scanner --config i18next-scanner.config.js"
},
-5
View File
@@ -1,5 +0,0 @@
// .env.pro
VITE_APP_MODE=pro
VITE_APP_TITLE=My Production App
VITE_API_BASE_URL=https://api.production.example.com
@@ -1,18 +0,0 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs','public','code-snippet','ace-editor'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
@@ -1,25 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
public/tinymce
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
-11
View File
@@ -1,11 +0,0 @@
# `businessEntry`
> TODO: description
## Usage
```
const businessEntry = require('businessEntry');
// TODO: DEMONSTRATE API
```
@@ -1,7 +0,0 @@
'use strict';
const businessEntry = require('..');
const assert = require('assert').strict;
assert.strictEqual(businessEntry(), 'Hello from businessEntry');
console.info('businessEntry tests passed');
@@ -1,15 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/frontend/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>APIPark</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script src="/frontend/iconpark_eolink.js"></script>
<script src="/frontend/iconpark_apinto.js"></script>
</body>
</html>
@@ -1,17 +0,0 @@
{
"name": "business-entry",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": " vite --port 5000 --strictPort",
"build": "vite build ",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview --port 5000 --strictPort",
"serve": "vite preview --port 5000 --strictPort"
},
"dependencies": {
},
"devDependencies": {
}
}
@@ -1,9 +0,0 @@
export default {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {}
},
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

-158
View File
@@ -1,158 +0,0 @@
import '@core/App.css'
import { ConfigProvider } from 'antd';
import RenderRoutes from '@businessEntry/components/aoplatform/RenderRoutes';
import {BreadcrumbProvider} from "@common/contexts/BreadcrumbContext.tsx";
import { StyleProvider } from '@ant-design/cssinjs';
import zhCN from 'antd/locale/zh_CN';
import useInitializeMonaco from "@common/hooks/useInitializeMonaco";
import ThemeSwitcher from '@common/components/aoplatform/ThemeSwitcher'
const antdComponentThemeToken = {
token: {
// Seed Token,影响范围大
colorPrimary: '#3D46F2',
colorLink:'#3D46F2',
colorBorder:'#ededed',
colorText:'#333',
borderRadius: 4,
// 派生变量,影响范围小
colorBgContainer: '#fff',
colorPrimaryBg:'#EBEEF2',
colorTextQuaternary:'#BBB',
colorTextTertiary:'#999'
},
components:{
// 派生变量,影响范围小
Input:{
activeShadow:'none'
},
Select:{
activeShadow:'none'
},
Checkbox:{
activeShadow:'none'
},
Cascader:{
activeShadow:'none',
optionSelectedBg:'#EBEEF2',
optionHoverBg:'#EBEEF2'
},
Layout: {
bodyBg: '#17163E',
headerBg: 'transparent',
headerColor: '#333',
headerPadding: '10 20px',
lightSiderBg: 'transparent',
siderBg: 'transparent',
},
Breadcrumb:{
itemColor:'#666',
linkColor:'#666',
lastItemColor:'#333',
},
Table:{
headerBorderRadius:0,
headerSplitColor:'#ededed',
borderColor:'#ededed',
cellPaddingBlockMD:'10px',
cellPaddingInlineMD:'12px',
cellPaddingBlockSM:'8px',
cellPaddingInlineSM:'12px',
headerFilterHoverBg:'#EBEEF2',
headerSortActiveBg:'#F7F8FA',
headerSortHoverBg:'#F7F8FA',
fixedHeaderSortActiveBg:'#F7F8FA',
headerBg:'#F7F8FA',
rowHoverBg:'#EBEEF2'
},
Segmented:{
itemColor:'#333',
itemSelectedColor:'#333',
trackBg:'#f7f8fa',
trackPadding:0,
// itemHoverColor:'#EBEEF2',
itemActiveBg:'#EBEEF2',
itemHoverBg:'#EBEEF2',
itemSelectedBg:'#EBEEF2',
},
Tree:{
// titleHeight:30,
// fontSize:12,
directoryNodeSelectedBg:'#EBEEF2',
directoryNodeSelectedColor:'#333',
nodeSelectedBg:'#EBEEF2',
nodeHoverBg:'#EBEEF2'
},
Collapse:{
headerBg:'#f7f8fa',
headerPadding:"12px",
contentPadding:"0 10px 12px 10px"
},
Button:{
// paddingInline:8,
dangerShadow:'none',
defaultShadow:'none',
primaryShadow:'none'
},
Tabs:{
cardBg:'#EBEEF2',
cardHeight:42,
horizontalItemGutter:8,
horizontalItemPaddingSM:'12px 8px 8px 8px',
horizontalItemPadding:'12px 8px 8px 8px',
},
Menu:{
// itemBg:'#F7F8FA',
// subMenuItemBg:'#F7F8FA',
// itemMarginBlock:0,
// activeBarBorderWidth:0,
// itemSelectedColor:'#333',
// itemSelectedBg:'#EBEEF2',
// itemHoverBg:'#EBEEF2'
// itemHeight:'72px',
// darkItemBg:'transparent',
// itemBg:'transparent',
// itemSelectedBg:'transparent',
// darkItemSelectedBg:'transparent',
// subMenuItemBg:'transparent',
// itemActiveBg:'transparent',
// darkSubMenuItemBg:'transparent',
// activeBarHeight:'2px',
// activeBarBorderWidth:2
},
List:{
itemPadding:'8px 0'
},
Form:{
itemMarginBottom:10,
},
Alert:{
defaultPadding:'12px 16px'
},
Tag:{
defaultBg:"#f7f8fa"
},
}
}
function App() {
useInitializeMonaco()
return (
<StyleProvider hashPriority={"high"}>
<ConfigProvider
locale={zhCN}
wave={{disabled:true}}
theme={antdComponentThemeToken}>
<ThemeSwitcher />
<BreadcrumbProvider>
<RenderRoutes />
</BreadcrumbProvider>
</ConfigProvider>
</StyleProvider>
);
}
export default App
@@ -1,484 +0,0 @@
import { BrowserRouter as Router, Routes, Route, Navigate, Outlet } from 'react-router-dom';
import Login from "@core/pages/Login.tsx"
import BasicLayout from '@common/components/aoplatform/BasicLayout';
import {createElement, ReactElement,ReactNode,Suspense} from 'react';
import { v4 as uuidv4 } from 'uuid'
import {App, Skeleton} from "antd";
import ApprovalPage from "@core/pages/approval/ApprovalPage.tsx";
import {SystemProvider} from "@core/contexts/SystemContext.tsx";
import {useGlobalContext} from "@common/contexts/GlobalStateContext.tsx";
import {FC,lazy} from 'react';
import { TeamProvider } from '@core/contexts/TeamContext.tsx';
import SystemOutlet from '@core/pages/system/SystemOutlet.tsx';
import { DashboardProvider } from '@core/contexts/DashboardContext.tsx';
import { TenantManagementProvider } from '@market/contexts/TenantManagementContext.tsx';
type RouteConfig = {
path:string
component?:ReactElement
children?:(RouteConfig|false)[]
key:string
provider?:FC<{ children: ReactNode; }>
lazy?:unknown
}
const APP_MODE = import.meta.env.VITE_APP_MODE;
export type RouterParams = {
teamId:string
apiId:string
serviceId:string
clusterId:string;
memberGroupId:string
userGroupId:string
pluginName:string
moduleId:string
accessType:'project'|'team'|'service'
categoryId:string
tagId:string
dashboardType:string
dashboardDetailId:string
topologyId:string
appId:string
roleType:string
roleId:string
}
const PUBLIC_ROUTES:RouteConfig[] = [
{
path:'/',
component:<Login/>,
key: uuidv4(),
},
{
path:'/login',
component:<Login/>,
key: uuidv4()
},
{
path:'/',
component:<ProtectedRoute/>,
key: uuidv4(),
children:[
{
path:'approval/*',
component:<ApprovalPage />,
key:uuidv4()
},
{
path:'team',
component:<Outlet/>,
key: uuidv4(),
provider: TeamProvider,
children:[
{
path:'',
key: uuidv4(),
component: <Navigate to="list" />
},
{
path:'list',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamList.tsx'))
},
{
path:'inside/:teamId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamInsidePage.tsx')),
key: uuidv4(),
children:[
{
path:'member',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamInsideMember.tsx')),
},
{
path:'setting',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamConfig.tsx')),
},
]
}
]
},
{
path:'service',
component:<SystemOutlet />,
key: uuidv4(),
provider: SystemProvider,
children:[
{
path:'',
key:uuidv4(),
component:<Navigate to="list" />
},
{
path:'list',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemList.tsx')),
},
{
path:'list/:teamId',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemList.tsx')),
},
{
path:':teamId',
component:<Outlet/>,
key: uuidv4(),
children:[
{
path:'inside/:serviceId',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsidePage.tsx')),
children:[
{
path:'api',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideApiDocument.tsx')),
},
{
path:'router',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterList')),
},
{
path:'upstream',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/upstream/SystemInsideUpstreamContent.tsx')),
},
{
path:'document',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsideDocument.tsx')),
},
{
path:'subscriber',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsideSubscriber.tsx')),
children:[
]
},
{
path:'approval',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApproval.tsx')),
children:[
{
path:'',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApprovalList.tsx')),
},
{
path:'*',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApprovalList.tsx')),
}
]
},
{
path:'topology',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemTopology.tsx')),
key: uuidv4(),
children:[
]
},
{
path:'publish',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublish.tsx')),
children:[
{
path:'',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublishList.tsx')),
},
{
path:'*',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublishList.tsx')),
}
]
},
{
path:'setting',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemConfig.tsx')),
children:[
]
},
]
}
]
}
]
},{
path:'datasourcing',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideDashboardSetting.tsx')),
},
{
path:'cluster',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideCluster.tsx')),
},
{
path:'cert',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideCert.tsx')),
},
{
path:'serviceHub',
component:<Outlet />,
key:uuidv4(),
children:[
{
path:'',
key: uuidv4(),
component: <Navigate to="list" />
},
{
path:'list',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/ServiceHubList.tsx')),
},
{
path:'detail/:serviceId',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/ServiceHubDetail.tsx')),
}]
},
{
path:'commonsetting',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/common/CommonPage.tsx')),
key:uuidv4(),
},
{
path:'consumer',
component:<Outlet />,
provider:TenantManagementProvider,
key:uuidv4(),
children:[
{
path:'',
key:uuidv4(),
component:<Navigate to="list" />
},
{
path:':teamId/inside/:appId',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsidePage.tsx')),
children:[
{
path:'service',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsideService.tsx')),
},
{
path:'authorization',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsideAuth.tsx')),
},
{
path:'setting',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementAppSetting.tsx')),
},
]
},
{
path:'list',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ServiceHubManagement.tsx')),
},
{
path:'list/:teamId',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ServiceHubManagement.tsx')),
},
]
},
{
path:'member',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberPage.tsx')),
children:[
{
path:'',
key:uuidv4(),
component:<Navigate to="list" />
},
{
path:'list',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberList.tsx')),
},
{
path:'list/:memberGroupId',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberList.tsx')),
}
]
},
{
path:'role',
key:uuidv4(),
component:<Outlet></Outlet>,
children:[
{
path: '',
key: uuidv4(),
component: <Navigate to="list" />
},
{
path:'list',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleList.tsx')),
},
{
path:':roleType/config',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleConfig.tsx')),
},
{
path:':roleType/config/:roleId',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleConfig.tsx')),
}
]
},
APP_MODE === 'pro' &&{
path:'openapi',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@openApi/pages/OpenApiList.tsx')),
key:uuidv4(),
},
{
path:'assets',
component:<p></p>,
key:uuidv4()
},
APP_MODE === 'pro' &&{
path:'analytics',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/Dashboard.tsx')),
key:uuidv4(),
children:[
{
path:':dashboardType',
component:<Outlet/>,
key:uuidv4(),
provider:DashboardProvider,
children:[
{
path:'list',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardList.tsx')),
key:uuidv4()
},
{
path:'detail/:dashboardDetailId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardDetail.tsx')),
key:uuidv4()
},
]
},
]
},
{
path:'systemrunning',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@systemRunning/pages/SystemRunning.tsx')),
key:uuidv4()
},
{
path:'template/:moduleId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '../../../../common/src/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
key:uuidv4()
},
{
path:'logsettings/*',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/logsettings/LogSettings.tsx')),
key: uuidv4(),
children:[{
path:'template/:moduleId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
key:uuidv4()
}]
},
APP_MODE ==='pro' && {
path:'resourcesettings/*',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/resourcesettings/ResourceSettings.tsx')),
key: uuidv4(),
children:[{
path:'template/:moduleId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
key:uuidv4()
}]
},
{
path:'userProfile/*',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/UserProfile.tsx')),
key:uuidv4(),
children:[{
path:'changepsw',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/ChangePsw.tsx')),
key:uuidv4()
}]
}
]
},
]
const RenderRoutes = ()=> {
return (
<App className="h-full" message={{ maxCount: 1 }}>
<Router>
<Routes>
{generateRoutes(PUBLIC_ROUTES)}
</Routes>
</Router>
</App>
)
}
const generateRoutes = (routerConfig: RouteConfig[]) => {
return routerConfig?.map((route: RouteConfig) => {
let routeElement;
if (route.lazy) {
const LazyComponent = route.lazy as React.ExoticComponent<unknown>;
routeElement = (
<Suspense fallback={ <div className=''><Skeleton className='m-btnbase w-calc-100vw-minus-padding-r' active /></div>}>
{route.provider ? (
createElement(route.provider, {}, <LazyComponent />)
) : (
<LazyComponent />
)}
</Suspense>
);
} else {
routeElement = route.provider ? (
createElement(route.provider, {}, route.component)
) : (
route.component
);
}
return (
<Route
key={route.key}
path={route.path}
element={routeElement}
>
{route.children && generateRoutes(route.children as RouteConfig[])}
</Route>
);
}
)
}
// 保护的路由组件
function ProtectedRoute() {
const {state} = useGlobalContext()
return state.isAuthenticated? <BasicLayout project="core" /> : <Navigate to="/login" />;
}
export default RenderRoutes
@@ -1,27 +0,0 @@
import {StrictMode} from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import '@core/index.css'
import {GlobalProvider} from "@common/contexts/GlobalStateContext.tsx";
async function initializeApp() {
try {
// 初始化行为
// await fetchInitialConfig(); // 示例:获取初始配置
// 异步操作完成后,渲染React应用
ReactDOM.createRoot(document.getElementById('root')!).render(
<StrictMode>
<GlobalProvider>
<App />
</GlobalProvider>
</StrictMode>,
);
} catch (error) {
console.error('Initialization failed:', error);
// 处理初始化失败的情况,比如渲染一个错误界面
}
}
// 执行初始化
initializeApp();
-1
View File
@@ -1 +0,0 @@
/// <reference types="vite/client" />
@@ -1,17 +0,0 @@
// start-vite.js// start-vite.js
import { exec } from 'child_process';
const viteProcess = exec('pnpm run build');
viteProcess.stdout.on('data', (data) => {
console.log(data.toString());
});
viteProcess.stderr.on('data', (data) => {
console.error(data.toString());
});
viteProcess.on('close', (code) => {
console.log(`Vite process exited with code ${code}`);
});
@@ -1,33 +0,0 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": true,
"paths": {
"@core/*": ["../core/src/*"],
"@common/*": ["../common/src/*"],
"@market/*": ["../market/src/*"],
"@dashboard/*": ["../dashboard/src/*"],
"@openApi/*": ["../openApi/src/*"],
"@systemRunning/*": ["../systemRunning/src/*"],
"@businessEntry/*": ["./src/*"],
},
},
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../common/src/components/aoplatform/ScrollableSection.tsx", "../common/src/utils/postcat.tsx", "../common/src/utils/curl.ts", "../common/src/components/aoplatform/ResetPsw.tsx", "../common/src/components/aoplatform/SubscribeApprovalModalContent.tsx", "src/components/aoplatform/RenderRoutes.tsx", "../common/src/components/aoplatform/PublishApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePage.tsx", "../common/src/const/type.ts", "../common/src/components/aoplatform/intelligent-plugin", "../common/src/const/domain"],
"references": [{ "path": "./tsconfig.node.json" }]
}
@@ -1,10 +0,0 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
@@ -1,80 +0,0 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars';
import tailwindcss from 'tailwindcss';
import autoprefixer from 'autoprefixer';
export default defineConfig({
cacheDir: './node_modules/.vite',
build:{
outDir:'../../dist',
sourcemap: false,
chunkSizeWarningLimit: 50000,
cacheDir: './node_modules/.vite',
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
// 针对 pnpm 和 Monorepo 特殊处理
if (id.includes('.pnpm')) {
const segments = id.split(path.sep);
const packageName = segments[segments.indexOf('.pnpm') + 1].split('@')[0];
return packageName;
}
}
},
},
css: {
postcss: {
plugins: [
tailwindcss(path.resolve(__dirname, '../common/tailwind.config.js')),
autoprefixer
],
},
preprocessorOptions: {
less: {
javascriptEnabled: true,
},
},
modules:{
localsConvention:"camelCase",
generateScopedName:"[local]_[hash:base64:2]"
}
},
plugins: [react(),
dynamicImportVars({
include:["src"],
exclude:[],
warnOnError:false
}),
],
resolve: {
alias: [
{ find: /^~/, replacement: '' },
{ find: '@common', replacement: path.resolve(__dirname, '../common/src') },
{ find: '@market', replacement: path.resolve(__dirname, '../market/src') },
{ find: '@core', replacement: path.resolve(__dirname, '../core/src') },
{ find: '@dashboard', replacement: path.resolve(__dirname, '../dashboard/src') },
{ find: '@openApi', replacement: path.resolve(__dirname, '../openApi/src') },
{ find: '@systemRunning', replacement: path.resolve(__dirname, '../systemRunning/src') },
{ find: '@businessEntry', replacement: path.resolve(__dirname, './src') },
]
},
server: {
proxy: {
'/api/v1': {
// target: 'http://uat.apikit.com:11204/mockApi/aoplatform/',
target: 'http://172.18.166.219:8288/',
changeOrigin: true,
},
'/api2/v1': {
// target: 'http://uat.apikit.com:11204/mockApi/aoplatform/',
target: 'http://172.18.166.219:8288/',
changeOrigin: true,
}
}
},
logLevel:'info'
})
File diff suppressed because one or more lines are too long
@@ -1,180 +1,162 @@
import {
MenuProps,
App,
Button,
ConfigProvider,
Dropdown} from 'antd';
import { Outlet, useLocation, useNavigate} from "react-router-dom";
import {
MenuProps,
App,
Button,
ConfigProvider,
Dropdown
} from 'antd';
import { Outlet, useLocation, useNavigate } from "react-router-dom";
import Logo from '@common/assets/layout-logo.png';
import AvatarPic from '@common/assets/default-avatar.png'
import { useEffect, useMemo, useState} from "react";
import { useCallback, useEffect, useMemo, useState} from "react";
import { useGlobalContext } from '@common/contexts/GlobalStateContext.tsx';
import { PERMISSION_DEFINITION } from '@common/const/permissions.ts';
import { BasicResponse, RESPONSE_TIPS, routerKeyMap, STATUS_CODE } from '@common/const/const.tsx';
import { UserInfoType } from '@common/const/type.ts';
import { useFetch } from '@common/hooks/http.ts';
import { ProjectFilled } from '@ant-design/icons';
import { getNavItem } from '@common/utils/navigation';
import { getNavItem, transformMenuData } from '@common/utils/navigation';
import { Icon } from '@iconify/react';
import { $t } from '@common/locales';
import { ProConfigProvider, ProLayout } from '@ant-design/pro-components';
import LanguageSetting from './LanguageSetting';
import { usePluginSlotHub } from '@common/contexts/PluginSlotHubContext';
const APP_MODE = import.meta.env.VITE_APP_MODE;
export type MenuItem = Required<MenuProps>['items'][number];
const themeToken = {
bgLayout:'#17163E;',
header: {
heightLayoutHeader:72
},
pageContainer:{
paddingBlockPageContainerContent:0,
paddingInlinePageContainerContent:0,
}
bgLayout: '#17163E;',
header: {
heightLayoutHeader: 72
},
pageContainer: {
paddingBlockPageContainerContent: 0,
paddingInlinePageContainerContent: 0,
}
}
function BasicLayout({project = 'core'}:{project:string}){
const navigator = useNavigate()
const location = useLocation()
const currentUrl = location.pathname
const { state,accessData,checkPermission,accessInit,dispatch,resetAccess,getGlobalAccessData} = useGlobalContext()
const [pathname, setPathname] = useState(currentUrl); const mainPage = project === 'core' ?'/service/list':'/serviceHub/list'
const navigator = useNavigate()
const location = useLocation()
const currentUrl = location.pathname
const { state,accessData,checkPermission,accessInit,dispatch,resetAccess,getGlobalAccessData, menuList} = useGlobalContext()
const [pathname, setPathname] = useState(currentUrl);
const mainPage = project === 'core' ?'/service/list':'/serviceHub/list'
const [menuItems, setMenuItems] = useState<MenuProps['items']>();
const pluginSlotHub = usePluginSlotHub()
const TOTAL_MENU_ITEMS:MenuProps['items'] = useMemo(() => [
getNavItem($t('工作空间'), 'workspace','/guide/page',<Icon icon="ic:baseline-space-dashboard" width="18" height="18"/>, [
getNavItem(<a>{$t('首页')}</a>, 'guide','/guide/page',<Icon icon="ic:baseline-home" width="18" height="18"/>,undefined,undefined,'all'),
getNavItem(<a>{$t('服务')}</a>, 'service','/service',<Icon icon="ic:baseline-blinds-closed" width="18" height="18"/>,undefined,undefined,'all'),
getNavItem(<a>{$t('消费者')}</a>, 'consumer','/consumer',<Icon icon="ic:baseline-apps" width="18" height="18"/>,undefined,undefined,'all'),
getNavItem(<a>{$t('团队')}</a>, 'team','/team',<Icon icon="ic:baseline-people-alt" width="18" height="18"/>,undefined,undefined,'all'),
]),
getNavItem($t('API 市场'), 'serviceHub','/serviceHub',<Icon icon="ic:baseline-hub" width="18" height="18"/>,undefined,undefined,'system.api_portal.api_portal.view'),
useEffect(()=>{
const newMenu = transformMenuData(menuList)
setMenuItems(newMenu);
},[menuList, state.language,accessInit])
getNavItem($t('仪表盘'), 'mainPage', APP_MODE === 'pro' ? '/analytics' : '/analytics/total',<Icon icon="ic:baseline-bar-chart" width="18" height="18"/>,[
getNavItem(<a >{$t('运行视图')}</a>, 'analytics',APP_MODE === 'pro' ? '/analytics' : '/analytics/total' ,<ProjectFilled />,undefined,undefined,'system.analysis.run_view.view'),
APP_MODE === 'pro' ? getNavItem(<a >{$t('系统拓扑图')}</a>, 'systemrunning','/systemrunning',<ProjectFilled />,undefined,undefined,'system.dashboard.systemrunning.view') : null,
],undefined,'system.analysis.run_view.view'),
getNavItem($t('系统设置'), 'operationCenter','/commonsetting',<Icon icon="ic:baseline-settings" width="18" height="18"/>, [
getNavItem($t('系统'), 'serviceHubSetting','/commonsetting',null,[
getNavItem(<a>{$t('常规')}</a>, 'commonsetting','/commonsetting',<Icon icon="ic:baseline-hub" width="18" height="18"/>,undefined,undefined,'system.api_market.service_classification.view'),
getNavItem(<a>{$t('API 网关')}</a>, 'cluster','/cluster',<Icon icon="ic:baseline-device-hub" width="18" height="18"/>,undefined,undefined,'system.settings.api_gateway.view'),
getNavItem(<a>{$t('AI 模型')}</a>, 'aisetting','/aisetting',<Icon icon="hugeicons:ai-network" width="18" height="18"/>,undefined,undefined,'system.settings.api_gateway.view'),
],undefined,'system.api_market.service_classification.view'),
getNavItem($t('用户'), 'organization','/member',null,[
getNavItem(<a>{$t('账号')}</a>, 'member','/member',<Icon icon="ic:baseline-people-alt" width="18" height="18"/>,undefined,undefined,'system.settings.account.view'),
getNavItem(<a>{$t('角色')}</a>, 'role','/role',<Icon icon="ic:baseline-verified-user" width="18" height="18"/>,undefined,undefined,'system.organization.role.view'),
],undefined,''),
getNavItem($t('集成'), 'maintenanceCenter','/datasourcing', null, [
getNavItem(<a>{$t('数据源')}</a>, 'datasourcing','/datasourcing',<Icon icon="ic:baseline-monitor-heart" width="18" height="18"/>,undefined,undefined,'system.settings.data_source.view'),
getNavItem(<a>{$t('证书')}</a>, 'cert','/cert',<Icon icon="ic:baseline-security" width="18" height="18"/>,undefined,undefined,'system.settings.ssl_certificate.view'),
getNavItem(<a>{$t('日志')}</a>, 'logsettings','/logsettings',<Icon icon="ic:baseline-sticky-note-2" width="18" height="18"/>,undefined,undefined,'system.settings.log_configuration.view'),
APP_MODE === 'pro' ? getNavItem(<a>{$t('资源')}</a>, 'resourcesettings','/resourcesettings',null,undefined,undefined,'system.partition.self.view'):null,
APP_MODE === 'pro' ? getNavItem(<a>{$t('Open API')}</a>, 'openapi','/openapi',null,undefined,undefined,'system.openapi.self.view'):null,
]),
]),
],[state.language,accessInit])
useEffect(() => {
if (currentUrl === '/') {
navigator(mainPage)
}
}, [currentUrl]);
useEffect(() => {
if(currentUrl === '/'){
navigator(mainPage)
}
}, [currentUrl]);
const headerMenuData = useMemo(() => {
// 判断权限
const hasAccess = (access: unknown) => checkPermission(access as keyof typeof PERMISSION_DEFINITION[0]);
const headerMenuData = useMemo(() => {
// 判断权限
const hasAccess = (access: unknown) => checkPermission(access as keyof typeof PERMISSION_DEFINITION[0]);
// 过滤菜单项
const filterMenu = (menu: Array<{ [k: string]: unknown }>) => {
return [...menu]
.filter(x => x) // 过滤掉空数据
.map((item: any) => {
if (item.routes && item.routes.length > 0) {
// 递归处理子菜单
const filteredRoutes: Array<{ [k: string]: unknown }> = filterMenu(item.routes);
if(filteredRoutes.length === 0){
return false
}
return {...item, routes: filteredRoutes};
}
// 处理没有 routes 的菜单项
if (item.access) {
return (item.access === 'all' || hasAccess(item.access)) ? item : null;
}
// 过滤菜单项
const filterMenu = (menu: Array<{ [k: string]: unknown }>) => {
return [...menu]
.filter(x => x) // 过滤掉空数据
.map((item: any) => {
if (item.routes && item.routes.length > 0) {
// 递归处理子菜单
const filteredRoutes: Array<{ [k: string]: unknown }> = filterMenu(item.routes);
if (filteredRoutes.length === 0) {
return false
}
return { ...item,routes: filteredRoutes,name:$t(item.name) };
}
// 处理没有 routes 的菜单项
if (item.access) {
return (item.access === 'all' || hasAccess(item.access)) ? {...item,name:$t(item.name)} : null;
}
// 如果没有 access 和 routes,则保留
return item;
return {...item,name:$t(item.name) };
})
.filter(x => x); // 过滤掉处理后为 null 的项
};
// 初始过滤操作
const res = [...TOTAL_MENU_ITEMS]!.filter(x => x).map((x: any) => (x.routes ? { ...x, routes: filterMenu(x.routes) } : x));
const res = [...(menuItems || [])]!.filter(x => x).map((x: any) => (x.routes ? { ...x,name:$t(x.name), routes: filterMenu(x.routes) } : {...x,name:$t(x.name)}));
// 返回处理后的数据
return { path: '/', routes: res.map(x=> ({...x, routes: x.routes?.filter(x=> (x.access || x.routes?.length > 0))})).filter(x=> (x.access || x.routes?.length > 0)) };
}, [accessData, state.language]);
}, [accessData, state.language,menuItems]);
const { message } = App.useApp()
const [userInfo,setUserInfo] = useState<UserInfoType>()
const {fetchData} = useFetch()
const navigate = useNavigate();
const getUserInfo = ()=>{
fetchData<BasicResponse<{profile:UserInfoType}>>('account/profile',{method:'GET'})
.then(response=>{
const {code,data,msg} = response
if(code === STATUS_CODE.SUCCESS){
setUserInfo(data.profile)
dispatch({type:'UPDATE_USERDATA',userData:data.profile})
}else{
message.error(msg || $t(RESPONSE_TIPS.error))
}
})
}
const getUserInfo = () => {
fetchData<BasicResponse<{ profile: UserInfoType }>>('account/profile', { method: 'GET' })
.then(response => {
const { code, data, msg } = response
if (code === STATUS_CODE.SUCCESS) {
setUserInfo(data.profile)
dispatch({ type: 'UPDATE_USERDATA', userData: data.profile })
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
})
}
useEffect(() => {
getUserInfo()
getGlobalAccessData()
}, []);
const logOut = ()=>{
fetchData<BasicResponse<null>>('account/logout',{method:'GET'}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
dispatch({type:'LOGOUT'})
resetAccess()
// message.success(msg || $t(RESPONSE_TIPS.logoutSuccess))
navigate('/login')
}else{
message.error(msg ||$t(RESPONSE_TIPS.error))
}
})
}
useEffect(() => {
getUserInfo()
getGlobalAccessData()
}, []);
const items: MenuProps['items'] = useMemo(() => [
userInfo?.type !== 'guest' && {
key: '2',
label: (
<Button key="changePsw" type="text" className="flex items-center p-0 bg-transparent border-none " onClick={()=>navigator('/userProfile/changepsw')}>
{$t('账号设置')}
</Button>)
},
{
key: '3',
label: (
<Button key="logout" type="text" className="flex items-center p-0 bg-transparent border-none " onClick={logOut}>
{$t('退出登录')}
</Button>)
},
].filter(Boolean), [userInfo]);
const logOut = () => {
fetchData<BasicResponse<null>>('account/logout', { method: 'GET' }).then(response => {
const { code, msg } = response
if (code === STATUS_CODE.SUCCESS) {
dispatch({ type: 'LOGOUT' })
resetAccess()
// message.success(msg || $t(RESPONSE_TIPS.logoutSuccess))
navigate('/login')
} else {
message.error(msg || $t(RESPONSE_TIPS.error))
}
})
}
const items: MenuProps['items'] = useMemo(() => [
userInfo?.type !== 'guest' && {
key: '2',
label: (
<Button key="changePsw" type="text" className="flex items-center p-0 bg-transparent border-none " onClick={() => navigator('/userProfile/changepsw')}>
{$t('账号设置')}
</Button>)
},
{
key: '3',
label: (
<Button key="logout" type="text" className="flex items-center p-0 bg-transparent border-none " onClick={logOut}>
{$t('退出登录')}
</Button>)
},
].filter(Boolean), [userInfo]);
const actionRender =useMemo( ()=>{
return [
<LanguageSetting />,
<Button className=" text-[#ffffffb3] hover:text-[#fff] border-none" type="default" ghost onClick={()=>{window.open('https://docs.apipark.com','_blank')}}>
<span className='flex items-center gap-[8px]'> <Icon icon="ic:baseline-help" width="14" height="14"/>{$t('文档')}</span>
</Button> ,
...((pluginSlotHub.getSlot('basicLayoutAfterBtns') as unknown[] )||[] )
]
},[pluginSlotHub.getSlot('basicLayoutAfterBtns') ])
return(
@@ -226,12 +208,7 @@ const themeToken = {
actionsRender={(props) => {
if (props.isMobile) return [];
if (typeof window === 'undefined') return [];
return [
<LanguageSetting />,
<Button className=" text-[#ffffffb3] hover:text-[#fff] border-none" type="default" ghost onClick={()=>{window.open('https://docs.apipark.com','_blank')}}>
<span className='flex items-center gap-[8px]'> <Icon icon="ic:baseline-help" width="14" height="14"/>{$t('文档')}</span>
</Button>
];
return actionRender;
}}
headerTitleRender={() => (
<div className="w-[192px] flex items-center">
@@ -243,7 +220,7 @@ const themeToken = {
</div>
)}
logo={Logo}
pageTitleRender={()=>$t('APIPark - 企业API数据开放平台')}
pageTitleRender={()=>$t('APIPark')}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
}}
@@ -37,13 +37,13 @@ const InsidePage:FC<InsidePageProps> = ({showBanner=true,pageTitle,tagList,showB
// <div className="h-full flex flex-col flex-1 overflow-hidden bg-[#f7f8fa]">
<div className={`h-full flex flex-col flex-1 overflow-hidden ${className}`}>
{ showBanner && <div className={`border-[0px] mr-PAGE_INSIDE_X ${showBorder ? 'border-b-[1px] border-solid border-BORDER' : ''} ${headerClassName}`}>
<div className="mb-[30px]">
{!pageTitle && !description && !backUrl &&!customBtn ? <></>: <div className="mb-[30px]">
{backUrl &&<div className="text-[18px] leading-[25px] mb-[12px]">
<Button type="text" onClick={goBack}><ArrowLeftOutlined className="max-h-[14px]" />{$t('返回')}</Button>
</div>}
<div className="flex justify-between mb-[20px] items-center ">
<div className="flex items-center gap-TAG_LEFT ">
<p className="text-theme text-[26px] ">{pageTitle}</p>
<div className="text-theme text-[26px] ">{pageTitle}</div>
{tagList && tagList?.length > 0 && tagList?.map((tag)=>{
return ( <Tag key={tag.label as string} bordered={false} >{tag.label}</Tag>)
})}
@@ -53,10 +53,10 @@ const InsidePage:FC<InsidePageProps> = ({showBanner=true,pageTitle,tagList,showB
}}>{btnTitle}</Button></WithPermission>}
{customBtn}
</div>
<p >
<div >
{description}
</p>
</div>
</div>
</div>}
</div>}
<div className={`h-full ${scrollPage ? 'overflow-hidden' : 'overflow-auto'} ${contentClassName || ''}`}>{children}</div>
</div>
@@ -1,27 +1,23 @@
import { GetProp, TransferProps, TreeDataNode, theme, Transfer, Tree, Spin } from "antd";
import { DataNode, TreeProps } from "antd/es/tree";
import { TransferProps, TreeDataNode, Tree, Spin, Input, Empty } from "antd";
import { DataNode } from "antd/es/tree";
import { Ref, forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
import { ApartmentOutlined, LoadingOutlined, UserOutlined } from "@ant-design/icons";
import { cloneDeep, debounce } from "lodash-es";
import { ColumnsType } from "antd/es/table";
import { $t } from "@common/locales";
import { useGlobalContext } from "@common/contexts/GlobalStateContext";
type TransferItem = GetProp<TransferProps, 'dataSource'>[number];
export type TransferTableProps<T> = {
request?:(k?:string)=>Promise<{data:T[],success:boolean}>
columns: ColumnsType<T>
primaryKey:string
onSelect:(selectedData:T[])=>void
onSelect:(selectedData:string[])=>void
tableType?:'member'|'api'
disabledData:string[]
searchPlaceholder?:string
}
export type TransferTableHandle<T> = {
selectedData: () => T[];
selectedRowKeys: () => React.Key[];
}
@@ -31,10 +27,6 @@ interface TreeTransferProps {
onChange: TransferProps['onChange'];
}
// Customize Table Transfer
const isChecked = (selectedKeys: React.Key[], eventKey: React.Key) =>
selectedKeys.includes(eventKey);
const generateTree = (
treeNodes: TreeDataNode[] = [],
checkedKeys: TreeTransferProps['targetKeys'] = [],
@@ -73,200 +65,104 @@ const generateTree = (
)
};
const TransferTree = (props)=>{
const { direction, token, tableHeight, dataSource, targetKeys, onItemSelect, onItemSelectAll,checkedKey,selectedKeys, filteredItems ,disabledData} = props;
const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
const getExpandedKeys = (newData:TreeDataNode[], expandedSet:Set<string> = new Set())=>{
newData.forEach((item)=>{
if(item.children && item.children.length > 0){
expandedSet.add(item.key)
getExpandedKeys(item.children,expandedSet)
}
})
return expandedSet
}
const treeData:TreeDataNode[] = useMemo(()=>{
const filteredSet = filteredItems && filteredItems.length > 0 ? new Set(filteredItems.map((x)=>x.id)) : new Set()
const res = dataSource && dataSource.length > 0 ? generateTree(dataSource, targetKeys,direction === 'right',disabledData,filteredSet) : []
setExpandedKeys(Array.from(getExpandedKeys(res)))
return res
},[
dataSource, targetKeys,direction ,disabledData,filteredItems
])
const onExpand: TreeProps['onExpand'] = (expandedKeysValue) => {
setExpandedKeys(expandedKeysValue as string[]);
};
return (
<div style={{ padding: token.paddingXS }}>
<Tree
className="icon-tree"
blockNode
checkable
showIcon
checkedKeys={direction === 'left' ? Array.from(new Set([...checkedKey,...disabledData])) : selectedKeys }
defaultExpandAll
expandedKeys={expandedKeys}
onExpand={onExpand}
height={tableHeight}
icon={(props)=> { return (props.type === 'member' ? <UserOutlined /> :<ApartmentOutlined /> )} }
treeData={treeData}
onCheck={(_checkedKeys, e:{checked: boolean, checkedNodes, node, event, halfCheckedKeys}) => {
if(e.checked){
onItemSelectAll( _checkedKeys, e.checked);
}else{
const checkedKeyArrFromTree = e.checkedNodes.map(node => node.key)
onItemSelectAll((checkedKey as string[]).filter(key => checkedKeyArrFromTree.indexOf(key) === -1),e.checked)
}
}}
onSelect={(_, { node: { key } }) => {
onItemSelect(key as string, !isChecked(checkedKey, key));
}}
/>
</div>
)
}
const MemberTransfer= forwardRef<TransferTableHandle<{[k:string]:unknown}>, TransferTableProps<{[k:string]:unknown}>>(
<T extends {[k:string]:unknown}>(props: TransferTableProps<T>, ref:Ref<TransferTableHandle<T>>) => {
const {request,columns,primaryKey,onSelect,tableType,disabledData = [],searchPlaceholder} = props
const [tableHeight, setTableHeight] = useState(window.innerHeight * 80 / 100 - 64 - 72 - 56 - 16 -3);
const [targetKeys, setTargetKeys] = useState<TreeTransferProps['targetKeys']>([]);
const [dataSource, setDataSource] = useState<DataNode[] >([])
const parentRef = useRef<HTMLDivElement>(null);
const [loading, setLoading] = useState<boolean>(false)
const {state} = useGlobalContext()
const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
const [searchWord, setSearchWord] = useState<string>('')
useEffect(()=>{
setTargetKeys(disabledData)
},[disabledData])
useImperativeHandle(ref, () =>({
selectedData: () => dataSource,
selectedRowKeys: () => targetKeys,}))
const onChange: TreeTransferProps['onChange'] = (keys) => {
onSelect?.(new Set(keys))
setTargetKeys(Array.from(new Set(keys)));
};
const translatedDataSource = useMemo(()=>{
const loop = (data: DataNode[]): DataNode[] =>
data?.map((item) => {
const strTitle:string = item.name === '所有成员' ? $t(item.name) as string : item.name as string;
const index = strTitle.indexOf(searchWord);
const beforeStr = strTitle.substring(0, index);
const afterStr = strTitle.slice(index + searchWord.length);
const title =
index > -1 ? (
<span className='w-[calc(100%-16px)] truncate' title={strTitle}>
{beforeStr}
<span className="text-theme">{searchWord}</span>
{afterStr}
</span>
) : (
<span className='w-[calc(100%-16px)] truncate' title={`${strTitle}`}>{strTitle}</span>
)
if (item.children) {
return {
...item,
title,
disableCheckbox:disabledData.indexOf(item.key as string) !== -1,
icon:<ApartmentOutlined />,
children: loop(item.children as T[]) };
}
const { token } = theme.useToken();
const transferDataSource: TransferItem[] = useMemo(()=>{
function flatten(list: TreeDataNode[] = [], res:TransferItem[]) {
list.forEach((item) => {
res.push({...item, title:item.title === '所有成员' ? $t((item as unknown as {title:string}).title):item.title }as TransferItem);
flatten(item.children,res);
});
}
const res:TransferItem[] =[]
flatten(dataSource,res);
return res
},[
dataSource, state.language
])
const translatedDataSource = useMemo(()=>dataSource.map((item)=>({
...item,
name:item.name === '所有成员' ? $t((item as unknown as {name:string}).name):item.name,
})),[dataSource, state.language])
let memo: Record<string, boolean> = {};
const handlerFilterOption = (inputValue: string, item: any, parentResult: boolean = false, childrenSet: Set<string> = new Set()): boolean => {
const cacheKey = `${inputValue}_${item.key}`;
if (memo[cacheKey]) {
return memo[cacheKey];
}
childrenSet.add(item.key);
let result = item.title.includes(inputValue) || parentResult
if (item.children) {
for (const child of item.children) {
if (handlerFilterOption(inputValue, child, result,childrenSet)) {
result = true;
}
}
}
if (result) {
memo[cacheKey] = result;
childrenSet.forEach((key) => {
memo[`${inputValue}_${key}`] = result;
return {
...item,
title,
icon:<UserOutlined />,
isLeaf:true,
disableCheckbox:disabledData.indexOf(item.key as string) !== -1
};
});
}
return result;
};
return loop(dataSource);
},[dataSource, state.language, searchWord])
const getInitExpandKeys = (data:T[], expandKeys:string[] = [])=>{
data.forEach((item)=>{
if(item.children?.length){
expandKeys.push(item.key as string)
getInitExpandKeys(item.children,expandKeys)
}
})
return expandKeys
}
const getDataSource = ()=>{
setLoading(true)
request && request().then((res)=>{
const {data,success} = res
setDataSource(success? data : [])
setExpandedKeys(getInitExpandKeys(success? data:[]))
}).finally(()=>{setLoading(false)})
}
}
useEffect(() => {
getDataSource()
const handleResize = () => {
setTableHeight(window.innerHeight * 80 / 100 - 64 - 72 - 56 - 16 -3)
};
const debouncedHandleResize = debounce(handleResize, 200);
// 监听窗口大小变化
window.addEventListener('resize', debouncedHandleResize);
handleResize();
return () => {
window.removeEventListener('resize', debouncedHandleResize);
};
}, []);
useEffect(() => {
getDataSource()
}, []);
return (
<div ref={parentRef}>
<Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={loading} className=''>
<Transfer
showSearch
onSearch={(dir)=>{
memo = {};
}}
listStyle={{width:'408px'}}
disabledData={disabledData}
filterOption={(inputValue: string, item: any) => handlerFilterOption(inputValue, item)}
targetKeys={targetKeys}
dataSource={transferDataSource}
className="tree-transfer"
render={(item) => item.title!}
showSelectAll={false}
onChange={onChange}
titles={['','']}
>
{({ direction, onItemSelect, selectedKeys,onItemSelectAll ,filteredItems}) => {
const treeProps = {
dataSource:translatedDataSource, direction, onItemSelect, selectedKeys,onItemSelectAll ,filteredItems,token,tableHeight,targetKeys,disabledData
}
if (direction === 'left') {
const checkedKey = [...selectedKeys, ...targetKeys as string[]];
return (
<TransferTree {...treeProps} checkedKey={checkedKey} />
);
}
if(direction === 'right'){
const checkedKey = [...selectedKeys,...targetKeys as string[]];
return (
<TransferTree {...treeProps} checkedKey={checkedKey} />
);
}
}}
</Transfer>
<Input className="mb-[10px]" placeholder={searchPlaceholder} onChange={(e)=>setSearchWord(e.target.value)} value={searchWord} />
<>{ translatedDataSource && translatedDataSource.length > 0 ? <Tree
checkable
expandedKeys={expandedKeys}
checkedKeys={targetKeys}
selectable={false}
onCheck={(e)=>{setTargetKeys(e);
onSelect(((e as string[])?.filter(x=>disabledData.indexOf(x as string) === -1))||[])}}
onExpand={setExpandedKeys}
treeData={translatedDataSource}
blockNode
showIcon
/>
: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/> }</>
</Spin>
</div>
);
@@ -0,0 +1,22 @@
import React, { useEffect, useState } from 'react';
import { Result, Skeleton } from 'antd';
const NotFound: React.FC = () => {
const [showPage, setShowPage] = useState<boolean>(false)
useEffect(()=>{
setTimeout(()=>setShowPage(true), 1000)
},[])
return (
<div className={`h-full w-full flex flex-1 align-middle ${showPage ? 'items-center' : ''}`}>
{ showPage ? <Result
className='w-full'
status="404"
title="404"
subTitle="Sorry, the page you visited does not exist."
/> : <Skeleton active /> }
</div>
)}
export default NotFound;
@@ -0,0 +1,95 @@
import {App, Form, Input, Row, Table} from "antd";
import {forwardRef, useEffect, useImperativeHandle, useMemo} from "react";
import {useFetch} from "@common/hooks/http.ts";
import {BasicResponse, PLACEHOLDER, PolicyPublishColumns, RESPONSE_TIPS, STATUS_CODE} from "@common/const/const.tsx";
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { $t } from "@common/locales";
import { useGlobalContext } from "@common/contexts/GlobalStateContext";
import { PolicyPublishModalHandle, PolicyPublishModalProps } from "@common/const/type";
export const PolicyPublishModalContent = forwardRef<PolicyPublishModalHandle,PolicyPublishModalProps>((props, ref) => {
const { message } = App.useApp()
const { data} = props
const [form] = Form.useForm();
const {fetchData} = useFetch()
const {state} = useGlobalContext()
const publish:()=>Promise<boolean | string | Record<string, unknown>> = ()=>{
return new Promise((resolve, reject)=>{
form.validateFields().then((value)=>{
const body = {...value, source:data.source}
fetchData<BasicResponse<null>>('strategy/global/data-masking/publish',{method: 'POST',eoBody:body,eoTransformKeys:['versionName']}).then(response=>{
const {code,msg} = response
if(code === STATUS_CODE.SUCCESS){
message.success(msg || $t(RESPONSE_TIPS.success))
resolve(response)
}else{
message.error(msg || $t(RESPONSE_TIPS.error))
reject(msg || $t(RESPONSE_TIPS.error))
}
}).catch((errorInfo)=> reject(errorInfo))
}).catch((errorInfo)=> reject(errorInfo))
})
}
useImperativeHandle(ref, ()=>({
publish,
})
)
useEffect(()=>{
form.setFieldsValue(data)
},[data])
const translatedPolicyColumns = useMemo(()=>PolicyPublishColumns.map((x)=>({
...x,
title: typeof x.title === 'string' ? $t(x.title) : x.title,
})),[state.language])
return (
<>
<WithPermission access=""><Form
className=" mx-auto"
form={form}
labelAlign='left'
layout='vertical'
scrollToFirstError
name="publishApprovalModalContent"
// labelCol={{span: 3}}
// wrapperCol={{span: 21}}
autoComplete="off"
>
<Form.Item
label={$t("发布名称")}
name='versionName'
rules={[{required: true,whitespace:true }]}
>
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
</Form.Item>
<Form.Item
label={$t("描述")}
name="desc"
>
<Input.TextArea className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
</Form.Item>
<Row className="mt-mbase pb-[8px] h-[32px] font-bold" ><span >{$t('策略列表')}</span></Row>
<Row className="mb-mbase ">
<Table
columns={translatedPolicyColumns}
bordered={true}
rowKey="name"
size="small"
dataSource={data.strategies || []}
pagination={false}
/>
{!data?.isPublish&& data?.unpublishMsg&& <p className="text-status_fail mt-[4px]">{data.unpublishMsg}</p>}
</Row>
</Form>
</WithPermission>
</>)
})
@@ -6,7 +6,7 @@ import {BasicResponse, FORM_ERROR_TIPS, PLACEHOLDER, RESPONSE_TIPS, STATUS_CODE,
import WithPermission from "@common/components/aoplatform/WithPermission.tsx";
import { SYSTEM_PUBLISH_ONLINE_COLUMNS } from "@core/const/system/const.tsx";
import { $t } from "@common/locales";
import { ApprovalRouteColumns, ApprovalStatusColorClass, ApprovalUpstreamColumns, ChangeTypeEnum } from "@common/const/approval/const";
import { ApprovalPolicyColumns, ApprovalRouteColumns, ApprovalStatusColorClass, ApprovalUpstreamColumns, ChangeTypeEnum } from "@common/const/approval/const";
import { useGlobalContext } from "@common/contexts/GlobalStateContext";
import { LoadingOutlined } from "@ant-design/icons";
import { SystemInsidePublishOnlineItems } from "@core/pages/system/publish/SystemInsidePublishOnline";
@@ -140,6 +140,22 @@ export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle
}
}),[state.language])
const translatedPolicyColumns = useMemo(()=>ApprovalPolicyColumns.map((x)=>{
return {
...x,
title: typeof x.title === 'string' ? $t(x.title) : x.title,
...(x.dataIndex === 'status' ? {
render:(_,entity)=> (
<span className={`${ApprovalStatusColorClass[entity.change as keyof typeof ApprovalStatusColorClass]} truncate block`}>
{$t(ChangeTypeEnum[entity.change as (keyof typeof ChangeTypeEnum)] || '-')}
</span>
)
}:{})
}
}),[state.language])
return (
<>
{!insidePage && <>
@@ -219,6 +235,16 @@ export const PublishApprovalModalContent = forwardRef<PublishApprovalModalHandle
/></Row>
</>
}
<Row className="mt-mbase pb-[8px] h-[32px] font-bold" ><span >{$t('策略列表')}</span></Row>
<Row className="mb-mbase ">
<Table
bordered={true}
columns={translatedPolicyColumns}
size="small"
rowKey="id"
dataSource={data.diffs?.strategies || []}
pagination={false}
/></Row>
{/* <Form.Item
label={$t("备注")}
name="remark"
@@ -8,64 +8,78 @@ import { Icon } from "@iconify/react/dist/iconify.js"
import { $t } from "@common/locales"
type TableBtnWithPermissionProps = {
btnTitle:string
access?:keyof typeof PERMISSION_DEFINITION[0],
tooltip?:string,
disabled?:boolean,
navigateTo?:string,
onClick?:(args?:unknown)=>void
className?:string
btnType:string
btnTitle: string
access?: keyof typeof PERMISSION_DEFINITION[0],
tooltip?: string,
disabled?: boolean,
navigateTo?: string,
onClick?: (args?: unknown) => void
className?: string
btnType: string
}
const TableIconName={
'add':'ic:baseline-add',
'edit':'ic:baseline-edit',
'delete':'ic:baseline-delete',
'remove':'ic:baseline-minus',
'copy':'ic:baseline-file-copy',
'view':'ic:baseline-remove-red-eye',
'publish':'ic:baseline-publish',
'approval':'ic:baseline-approval',
'stop':'ic:baseline-stop-circle',
'online':'ic:baseline-check-circle',
'cancel':'ic:baseline-cancel-schedule-send',
'refresh':'ic:baseline-refresh'
const TableIconName = {
'add': 'ic:baseline-add',
'edit': 'ic:baseline-edit',
'delete': 'ic:baseline-delete',
'remove': 'ic:baseline-minus',
'copy': 'ic:baseline-file-copy',
'view': 'ic:baseline-remove-red-eye',
'publish': 'ic:baseline-publish',
'approval': 'ic:baseline-approval',
'stop': 'ic:baseline-stop-circle',
'online': 'ic:baseline-check-circle',
'cancel': 'ic:baseline-cancel-schedule-send',
'refresh': 'ic:baseline-refresh',
'logs': 'hugeicons:google-doc'
}
// 表格操作栏按钮,受权限控制
const TableBtnWithPermission = ({btnTitle, access, tooltip, disabled, navigateTo, onClick,className,btnType}:TableBtnWithPermissionProps) => {
const [btnAccess, setBtnAccess] = useState<boolean>(false)
const {accessData,checkPermission,accessInit} = useGlobalContext()
const navigate = useNavigate()
const lastAccess = useMemo(()=>{
if(!accessInit) return false
if(!access) return true
return checkPermission(access)
},[access, accessData,checkPermission,accessInit])
const TableBtnWithPermission = ({ btnTitle, access, tooltip, disabled, navigateTo, onClick, className, btnType }: TableBtnWithPermissionProps) => {
useEffect(()=>{
access ? setBtnAccess(lastAccess) : setBtnAccess(true)
},[access, lastAccess])
const [btnAccess, setBtnAccess] = useState<boolean>(false)
const [btnStatus, setBtnStatus] = useState<boolean>(false)
const [closeToolTip, setCloseToolTip] = useState<boolean>(false)
const { accessData, checkPermission, accessInit } = useGlobalContext()
const navigate = useNavigate()
const lastAccess = useMemo(() => {
if (!accessInit) return false
if (!access) return true
return checkPermission(access)
}, [access, accessData, checkPermission, accessInit])
useEffect(() => {
access ? setBtnAccess(lastAccess) : setBtnAccess(true)
}, [access, lastAccess])
const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
setTimeout(() => {
setBtnStatus(false)
setCloseToolTip(true)
})
const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
navigateTo ? navigate(navigateTo) : onClick?.()
}, [navigateTo, navigate, onClick])
return (<>{
!btnAccess || (disabled&&tooltip) ?
<Tooltip placement="top" title={tooltip ?? $t('暂无(0)权限,请联系管理员分配。',[$t(btnTitle).toLowerCase()])}>
<Button type="text" disabled={true} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className}`} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18"/>} >{}</Button>
</Tooltip>
:
<Tooltip placement="top" title={$t(btnTitle)}>
<Button type="text" disabled={disabled} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className} `} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18"/>} onClick={handleClick}>{}</Button>
</Tooltip>
}</>
);
navigateTo ? navigate(navigateTo) : onClick?.()
}, [navigateTo, navigate, onClick])
const changeTooltipStatus = (open: boolean) => {
setBtnStatus(open)
if (closeToolTip) {
setBtnStatus(false)
setCloseToolTip(false)
}
}
return (<>{
!btnAccess || (disabled && tooltip) ?
<Tooltip placement="top" title={tooltip ?? $t('暂无(0)权限,请联系管理员分配。', [$t(btnTitle).toLowerCase()])}>
<Button type="text" disabled={true} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className}`} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18" />} >{ }</Button>
</Tooltip>
:
<Tooltip placement="top" title={$t(btnTitle)} trigger='hover' open={btnStatus} onOpenChange={changeTooltipStatus}>
<Button type="text" disabled={disabled} className={`h-[22px] border-none p-0 flex items-center bg-transparent ${className} `} key={btnType} icon={<Icon icon={TableIconName[btnType as keyof typeof TableIconName]} width="18" height="18" />} onClick={handleClick}>{ }</Button>
</Tooltip>
}</>
);
}
export default TableBtnWithPermission
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { Radio, DatePicker, GetProps, RadioChangeEvent } from 'antd';
import dayjs, { Dayjs } from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
@@ -12,69 +12,80 @@ export type RangeValue = [Dayjs | null, Dayjs | null] | null;
dayjs.extend(customParseFormat);
export type TimeRange = {
start:number|null
end:number|null
start: number | null
end: number | null
}
export type TimeRangeButton = ''| 'hour' | 'day' | 'threeDays' | 'sevenDays';
export type TimeRangeButton = '' | 'hour' | 'day' | 'threeDays' | 'sevenDays';
type TimeRangeSelectorProps = {
initialTimeButton?:TimeRangeButton,
initialDatePickerValue?:RangeValue
onTimeRangeChange?:(timeRange:TimeRange) =>void
hideTitle?:boolean
onTimeButtonChange:(time:TimeRangeButton) =>void
labelSize?:'small'|'default'
}
const TimeRangeSelector = (props:TimeRangeSelectorProps) => {
const {initialTimeButton,initialDatePickerValue,onTimeRangeChange,hideTitle,onTimeButtonChange,labelSize='default'} = props
initialTimeButton?: TimeRangeButton,
initialDatePickerValue?: RangeValue
onTimeRangeChange?: (timeRange: TimeRange) => void
hideTitle?: boolean
onTimeButtonChange: (time: TimeRangeButton) => void
labelSize?: 'small' | 'default'
bindRef?: any
hideBtns?: TimeRangeButton[]
defaultTimeButton?: TimeRangeButton
}
const TimeRangeSelector = (props: TimeRangeSelectorProps) => {
const { initialTimeButton, initialDatePickerValue, onTimeRangeChange, hideTitle, onTimeButtonChange, labelSize = 'default', bindRef, hideBtns = [], defaultTimeButton = 'hour' } = props
const [timeButton, setTimeButton] = useState(initialTimeButton || '');
const [datePickerValue, setDatePickerValue] = useState<RangeValue>(initialDatePickerValue || [null,null]);
const [datePickerValue, setDatePickerValue] = useState<RangeValue>(initialDatePickerValue || [null, null]);
useEffect(() => {
if (bindRef) {
bindRef({ reset });
}
}, [bindRef])
// 根据选择的时间范围计算开始和结束时间
const calculateTimeRange = (curBtn:'hour'|'day'|'threeDays'|'sevenDays') => {
const currentSecond = new Date().getTime() // 当前毫秒数时间戳
const currentMin = currentSecond - (currentSecond % (60 * 1000)) // 当前分钟数时间戳
let startMin = currentMin - 60 * 60 * 1000
const calculateTimeRange = (curBtn: TimeRangeButton) => {
const currentSecond = Math.floor(Date.now() / 1000); // 当前秒级时间戳
let startMin = currentSecond - 60 * 60
switch (curBtn) {
case 'hour': {
startMin = currentMin - 60 * 60 * 1000
break
}
case 'day': {
startMin = currentMin - 24 * 60 * 60 * 1000
break
}
case 'threeDays': {
startMin =
new Date(new Date().setHours(0, 0, 0, 0)).getTime() -
2 * 24 * 60 * 60 * 1000
break
}
case 'sevenDays': {
startMin =
new Date(new Date().setHours(0, 0, 0, 0)).getTime() -
6 * 24 * 60 * 60 * 1000
break
}
case 'hour': {
startMin = currentSecond - 60 * 60
break
}
case 'day': {
startMin = currentSecond - 24 * 60 * 60
break
}
case 'threeDays': {
startMin =
Math.floor(new Date().setHours(0, 0, 0, 0) / 1000) -
2 * 24 * 60 * 60
break
}
case 'sevenDays': {
startMin =
Math.floor(new Date().setHours(0, 0, 0, 0) / 1000) -
6 * 24 * 60 * 60
break
}
}
if (onTimeRangeChange) {
onTimeRangeChange({ start: startMin / 1000, end: currentMin / 1000 });
onTimeRangeChange({ start: startMin, end: currentSecond });
}
};
// 处理单选按钮的变化
const handleRadioChange = (e:RadioChangeEvent) => {
const handleRadioChange = (e: RadioChangeEvent) => {
setTimeButton(e.target.value);
onTimeButtonChange?.(e.target.value)
setDatePickerValue(null)
calculateTimeRange(e.target.value);
};
const reset = () => {
setTimeButton(defaultTimeButton)
calculateTimeRange(defaultTimeButton)
setDatePickerValue(null)
}
// 处理日期选择器的变化
const handleDatePickerChange = (dates: RangeValue) => {
setTimeButton(dates ? '' : 'hour')
onTimeButtonChange?.(dates ? '' : 'hour')
setTimeButton(dates ? '' : defaultTimeButton)
onTimeButtonChange?.(dates ? '' : defaultTimeButton)
setDatePickerValue(dates);
if (dates && Array.isArray(dates) && dates.length === 2) {
const [startDate, endDate] = dates;
@@ -84,34 +95,37 @@ const TimeRangeSelector = (props:TimeRangeSelectorProps) => {
onTimeRangeChange({ start, end });
}
}
if (!dates) {
calculateTimeRange(defaultTimeButton)
}
};
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
// Can not select days before today and today
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
// Can not select days before today and today
return current && current.valueOf() > dayjs().startOf('day').valueOf();
};
};
return (
<div className="flex flex-nowrap items-center pt-btnybase mr-btnybase">
{!hideTitle && <label className={`whitespace-nowrap `}>{$t('时间')}</label>}
<Radio.Group className="whitespace-nowrap" value={timeButton} onChange={handleRadioChange} buttonStyle="solid">
<Radio.Button value="hour">{$t('近1小时')}</Radio.Button>
<Radio.Button value="day">{$t('近24小时')}</Radio.Button>
<Radio.Button value="threeDays">{$t('近3天')}</Radio.Button>
<Radio.Button className="rounded-e-none" value="sevenDays">{$t('近7天')}</Radio.Button>
</Radio.Group>
{!hideTitle && <label className={`whitespace-nowrap `}>{$t('时间')}</label>}
<Radio.Group className="whitespace-nowrap" value={timeButton} onChange={handleRadioChange} buttonStyle="solid">
{hideBtns?.length && hideBtns.includes('hour') ? null : <Radio.Button value="hour">{$t('近1小时')}</Radio.Button>}
{hideBtns?.length && hideBtns.includes('day') ? null : <Radio.Button value="day">{$t('近24小时')}</Radio.Button>}
{hideBtns?.length && hideBtns.includes('threeDays') ? null : <Radio.Button value="threeDays">{$t('近3天')}</Radio.Button>}
{hideBtns?.length && hideBtns.includes('sevenDays') ? null : <Radio.Button className="rounded-e-none" value="sevenDays">{$t('近7天')}</Radio.Button>}
</Radio.Group>
<DatePicker.RangePicker
value={datePickerValue}
className="rounded-s-none ml-[-1px]"
className="rounded-s-none ml-[-1px]"
disabledDate={disabledDate}
onChange={handleDatePickerChange}
onOpenChange={(open)=>{
if(!open && datePickerValue && datePickerValue.length > 2){
setTimeButton('')
onTimeButtonChange?.('')
}
onOpenChange={(open) => {
if (!open && datePickerValue && datePickerValue.length > 2) {
setTimeButton('')
onTimeButtonChange?.('')
}
}}
/>
</div>
@@ -159,7 +159,32 @@ export const TranslateWord = ()=>{
{$t('地址')}
{$t('新增')}
{$t('申请方消费者')}
{$t('策略名称')}
{$t('优先级')}
{$t('筛选条件')}
{$t('处理数')}
{$t('数据格式')}
{$t('关键字')}
{$t('正则表达式')}
{$t('手机号')}
{$t('身份证号')}
{$t('银行卡号')}
{$t('金额')}
{$t('日期')}
{$t('局部显示')}
{$t('局部遮蔽')}
{$t('截取')}
{$t('替换')}
{$t('乱序')}
{$t('随机字符串')}
{$t('自定义字符串')}
{$t('请输入IP地址或CIDR范围,每条以换行分割')}
{$t('待更新')}
{$t('待删除')}
{$t('内容')}
{$t('调用地址')}
{$t('消费者 IP')}
{$t('鉴权名称')}
</>
)
}
@@ -39,7 +39,7 @@ const WithPermission = ({access, tooltip, children,disabled, showDisabled = true
{ cloneElement(children, {disabled:true})}
</Tooltip>}
{!editAccess && (children?.type !== Button && children?.type !== Upload && showDisabled) && <Tooltip title={tooltip ?? $t("暂无操作权限,请联系管理员分配。")}>
{ cloneElement(children, {disabled:true,okButtonProps:{disabled:true}})}
{ cloneElement(children, {disabled:true, onClick:(e)=>e.preventDefault(),okButtonProps:{disabled:true}})}
</Tooltip>}
</>
@@ -0,0 +1,79 @@
import { set } from 'lodash-es';
import { ExoticComponent, JSXElementConstructor, ReactElement, useEffect, useState } from 'react';
import { useBlocker, useLocation, useNavigate } from 'react-router-dom';
import { JSX } from 'react/jsx-runtime';
const withRouteGuard = (WrappedComponent: ExoticComponent<any> | JSXElementConstructor<any>, {
canActivate,
canLoad ,
canDeactivate,
deactivated,
pathPrefix
}: { pathPrefix?:string, canActivate?: () => Promise<boolean>; canLoad?: () => Promise<boolean>; canDeactivate?: () => Promise<boolean>; deactivated?: () => Promise<void>; } = {}) => {
return function RouteGuard(props: JSX.IntrinsicAttributes) {
const [isActivated, setIsActivated] = useState<boolean>(false);
const location = useLocation();
// check canActivate
const startLifecycle = async ()=>{
if(canActivate){
const activateRes = await canActivate();
setIsActivated(activateRes);
}else{
setIsActivated(true);
}
}
// check canDeactivate
const handleBeforeUnload =async (event: { preventDefault: () => void; returnValue: string; }) => {
const deactivateRes = canDeactivate? await canDeactivate():true;
if (!deactivateRes) {
event.preventDefault();
event.returnValue = '';
}
};
// 激活组件时的检查
useEffect(() => {
startLifecycle();
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
deactivated?.();
};
}, []);
const blocker = useBlocker((tx) => {
const currentPath = location.pathname;
const targetPath = tx.nextLocation.pathname;
if (pathPrefix && currentPath.startsWith(pathPrefix) && !targetPath.startsWith(pathPrefix) && canDeactivate) {
canDeactivate().then((res) => {
if(res){
return false;
}else{
return true;
}
})
} else {
return false
}
});
const checkCanLoad = async()=>{
const loadRes = await canLoad!();
!loadRes && setIsActivated(false);
}
useEffect(() => {
if (isActivated && canLoad) {
checkCanLoad()
}
}, [isActivated]);
return isActivated ? <WrappedComponent {...props}/> : null;
};
}
export default withRouteGuard;
@@ -58,7 +58,7 @@ import {
UPDATE_DATASETS_EVENT_EMITTER,
UPDATE_HISTORY_EVENT_EMITTER,
} from './constants'
import { useEventEmitterContextContext } from '@common/contexts/event-emitter'
import { useEventEmitterContextContext } from '@common/contexts/EventEmitterContext'
export type PromptEditorProps = {
instanceId?: string
@@ -30,7 +30,7 @@ import { $splitNodeContainingQuery } from '../../utils'
import { useOptions } from './hooks'
import type { PickerBlockMenuOption } from './menu'
// import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
import { useEventEmitterContextContext } from '@common/contexts/event-emitter'
import { useEventEmitterContextContext } from '@common/contexts/EventEmitterContext'
type ComponentPickerProps = {
triggerString: string
@@ -14,7 +14,7 @@ import { DELETE_CONTEXT_BLOCK_COMMAND } from './index'
// PortalToFollowElemContent,
// PortalToFollowElemTrigger,
// } from '@/app/components/base/portal-to-follow-elem'
import { useEventEmitterContextContext } from '@common/contexts/event-emitter'
import { useEventEmitterContextContext } from '@common/contexts/EventEmitterContext'
import { $t } from '@common/locales'
type ContextBlockComponentProps = {
@@ -14,7 +14,7 @@ import { DELETE_HISTORY_BLOCK_COMMAND } from './index'
// PortalToFollowElemContent,
// PortalToFollowElemTrigger,
// } from '@/app/components/base/portal-to-follow-elem'
import { useEventEmitterContextContext } from '@common/contexts/event-emitter'
import { useEventEmitterContextContext } from '@common/contexts/EventEmitterContext'
import { $t } from '@common/locales'
type HistoryBlockComponentProps = {
@@ -3,7 +3,7 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
import { textToEditorState } from '../utils'
import { CustomTextNode } from './custom-text/node'
import { CLEAR_HIDE_MENU_TIMEOUT } from './workflow-variable-block'
import { useEventEmitterContextContext } from '@common/contexts/event-emitter'
import { useEventEmitterContextContext } from '@common/contexts/EventEmitterContext'
export const PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER = 'PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER'
export const PROMPT_EDITOR_INSERT_QUICKLY = 'PROMPT_EDITOR_INSERT_QUICKLY'
@@ -13,6 +13,7 @@ export interface CodeboxApiRef {
formatCode: () => void
}
export type codeBoxLanguagesType = 'html' | 'json' | 'xml' | 'javascript' | 'css' | 'plaintext'|'yaml'
interface CodeboxProps {
options?: MonacoEditor.IStandaloneEditorConstructionOptions
value?: string
@@ -22,7 +23,7 @@ interface CodeboxProps {
height?: string | null
readOnly?: boolean
apiRef?: RefObject<CodeboxApiRef>
language?: 'html' | 'json' | 'xml' | 'javascript' | 'css' | 'plaintext'|'yaml'
language?: codeBoxLanguagesType
extraContent?:React.ReactNode
sx?:Record<string,unknown>
editorTheme?:'vs' | 'vs-dark' | 'hc-black'
@@ -295,7 +295,7 @@ export interface IconParkIconElement extends HTMLElement {
| 'apispace'
| 'auto-generate-api'
| 'compare-api'
| 'multi-protocal'
| 'multi-protocol'
| 'read-good'
| 'richdoc'
| 'mockapi'
@@ -241,6 +241,26 @@ export const ApprovalRouteColumns = [
}
]
export const ApprovalPolicyColumns = [
{
title:('名称'),
dataIndex:'name',
ellipsis:true,
},
{
title:('优先级'),
dataIndex:'priority',
ellipsis:true
},
{
title:('状态'),
dataIndex:'status',
}
]
export const ApprovalUpstreamColumns = [
{
title:('上游类型'),
@@ -67,6 +67,7 @@ export type PublishApprovalInfoType = {
diffs:{
apis:PublishApprovalApiItem[]
upstreams:PublishApprovalUpstreamItem[]
strategies:Array<{name:string, priority:number,statues:0|1}>
}
clusterPublishStatus?:SystemInsidePublishOnlineItems[],
error:string
+35 -3
View File
@@ -1,3 +1,5 @@
import { $t } from '@common/locales'
import { StrategyStatusColorClass, StrategyStatusEnum } from './policy/consts'
export type BasicResponse<T> = {
code:number
@@ -5,7 +7,6 @@ export type BasicResponse<T> = {
msg:string
}
export const STATUS_CODE = {
SUCCESS:0,
UNANTHORIZED:401,
@@ -18,7 +19,7 @@ export const STATUS_COLOR = {
}
// avoid changing route within ths same category
// TODO should be generated dynamically
export const routerKeyMap = new Map<string, string[]|string>([
['workspace',['consumer','service','team','guide']],
['my',['consumer','service','team']],
@@ -29,6 +30,7 @@ export const routerKeyMap = new Map<string, string[]|string>([
['maintenanceCenter',['aisetting','datasourcing','cluster','cert','logsettings','resourcesettings','openapi']
]])
export const COLUMNS_TITLE = {
operate : ''
@@ -45,6 +47,7 @@ export const routerKeyMap = new Map<string, string[]|string>([
startWithAlphabet:('英文数字下划线任意一种,首字母必须为英文'),
specialStartWithAlphabet:('支持字母开头、英文数字中横线下划线组合'),
onlyAlphabet:('字符非法,仅支持英文'),
ipAndCidr:'请输入IP地址或CIDR范围,每条以换行分割'
}
export const FORM_ERROR_TIPS = {
@@ -73,4 +76,33 @@ export const routerKeyMap = new Map<string, string[]|string>([
export const DATA_SHOW_TYPE_OPTIONS = [
{label:'列表', value:'list'},
{label:'块', value:'block'},
]
]
export const PolicyPublishColumns = [
{
title: ('策略名称'),
dataIndex: 'name',
ellipsis: true,
width: 160
},
{
title: ('优先级'),
dataIndex: 'priority',
width: 140,
ellipsis: true
},
{
title: ('状态'),
dataIndex: 'status',
width: 140,
render:(text:string)=> <span className={StrategyStatusColorClass[text as keyof typeof StrategyStatusColorClass]}>{$t(StrategyStatusEnum[text as keyof typeof StrategyStatusEnum])}</span>,
},
{
title: ('更新时间'),
dataIndex: 'optTime',
width: 182,
ellipsis: true,
},
]
+100 -50
View File
@@ -239,6 +239,31 @@ export const PERMISSION_DEFINITION = [
"anyOf": [{ "backend": ["system.settings.log_configuration.manager"] }]
}
},
"system.devops.policy.view": {
"granted": {
"anyOf": [{ "backend": ["system.settings.strategy.view"] }]
}
},
"system.devops.policy.add": {
"granted": {
"anyOf": [{ "backend": ["system.settings.strategy.manager"] }]
}
},
"system.devops.policy.edit": {
"granted": {
"anyOf": [{ "backend": ["system.settings.strategy.manager"] }]
}
},
"system.devops.policy.publish": {
"granted": {
"anyOf": [{ "backend": ["system.settings.strategy.manager"] }]
}
},
"system.devops.policy.delete": {
"granted": {
"anyOf": [{ "backend": ["system.settings.strategy.manager"] }]
}
},
"system.workspace.application.view_all": {
"granted": {
"anyOf": [{ "backend": ["system.workspace.application.view_all"] }]
@@ -276,252 +301,277 @@ export const PERMISSION_DEFINITION = [
},
"team.service.api_doc.view": {
"granted": {
"anyOf": [{ "backend": ["team.service.api_doc.view"] }]
"anyOf": [{ "backend": ["system.workspace.service.view_all","team.service.api_doc.view"] }]
}
},
"team.service.api_doc.add": {
"granted": {
"anyOf": [{ "backend": ["team.service.api_doc.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.api_doc.manager"] }]
}
},
"team.service.api_doc.edit": {
"granted": {
"anyOf": [{ "backend": ["team.service.api_doc.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.api_doc.manager"] }]
}
},
"team.service.service_intro.view": {
"granted": {
"anyOf": [{ "backend": ["team.service.service_intro.view"] }]
"anyOf": [{ "backend": ["system.workspace.service.view_all","team.service.service_intro.view"] }]
}
},
"team.service.service_intro.add": {
"granted": {
"anyOf": [{ "backend": [" team.service.service_intro.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.service_intro.manager"] }]
}
},
"team.service.service_intro.edit": {
"granted": {
"anyOf": [{ "backend": [" team.service.service_intro.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.service_intro.manager"] }]
}
},
"team.service.api_doc.import": {
"granted": {
"anyOf": [{ "backend": ["team.service.api_doc.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.api_doc.manager"] }]
}
},
"team.service.router.view": {
"granted": {
"anyOf": [{ "backend": ["team.service.api.view"] }]
"anyOf": [{ "backend": ["system.workspace.service.view_all","team.service.api.view"] }]
}
},
"team.service.router.add": {
"granted": {
"anyOf": [{ "backend": ["team.service.api.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.api.manager"] }]
}
},
"team.service.router.edit": {
"granted": {
"anyOf": [{ "backend": ["team.service.api.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.api.manager"] }]
}
},
"team.service.router.delete": {
"granted": {
"anyOf": [{ "backend": ["team.service.api.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.api.manager"] }]
}
},
"team.service.upstream.view": {
"granted": {
"anyOf": [{ "backend": ["team.service.upstream.view"] }]
"anyOf": [{ "backend": ["system.workspace.service.view_all","team.service.upstream.view"] }]
}
},
"team.service.upstream.add": {
"granted": {
"anyOf": [{ "backend": ["team.service.upstream.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.upstream.manager"] }]
}
},
"team.service.upstream.edit": {
"granted": {
"anyOf": [{ "backend": ["team.service.upstream.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.upstream.manager"] }]
}
},
"team.service.upstream.delete": {
"granted": {
"anyOf": [{ "backend": ["team.service.upstream.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.upstream.manager"] }]
}
},
"team.service.release.view": {
"granted": {
"anyOf": [{ "backend": ["team.service.release.view"] }]
"anyOf": [{ "backend": ["system.workspace.service.view_all","team.service.release.view"] }]
}
},
"team.service.release.add": {
"granted": {
"anyOf": [{ "backend": ["team.service.release.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.release.manager"] }]
}
},
"team.service.release.online": {
"granted": {
"anyOf": [{ "backend": ["team.service.release.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.release.manager"] }]
}
},
"team.service.release.stop": {
"granted": {
"anyOf": [{ "backend": ["team.service.release.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.release.manager"] }]
}
},
"team.service.release.cancel": {
"granted": {
"anyOf": [{ "backend": ["team.service.release.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.release.manager"] }]
}
},
"team.service.release.rollback": {
"granted": {
"anyOf": [{ "backend": ["team.service.release.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.release.manager"] }]
}
},
"team.service.release.delete": {
"granted": {
"anyOf": [{ "backend": ["team.service.release.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.release.manager"] }]
}
},
"team.service.release.approval": {
"granted": {
"anyOf": [{ "backend": ["team.service.release.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.release.manager"] }]
}
},
"team.service.subscription.view": {
"granted": {
"anyOf": [{ "backend": ["team.service.subscription.view"] }]
"anyOf": [{ "backend": ["system.workspace.service.view_all","team.service.subscription.view"] }]
}
},
"team.service.subscription.approval": {
"granted": {
"anyOf": [{ "backend": ["team.service.subscription.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.subscription.manager"] }]
}
},
"team.service.subscription.add": {
"granted": {
"anyOf": [{ "backend": ["team.service.subscription.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.subscription.manager"] }]
}
},
"team.service.subscription.delete": {
"granted": {
"anyOf": [{ "backend": ["team.service.subscription.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.service.subscription.manager"] }]
}
},
"team.service.service.view": {
"granted": {
"anyOf": [{ "backend": ["team.team.service.view"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","system.workspace.service.view_all","team.team.service.view"] }]
}
},
"team.service.service.add": {
"granted": {
"anyOf": [{ "backend": ["team.team.service.manager","team.service.service.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.team.service.manager","team.service.service.manager"] }]
}
},
"team.service.service.edit": {
"granted": {
"anyOf": [{ "backend": ["team.team.service.manager","team.service.service.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.team.service.manager","team.service.service.manager"] }]
}
},
"team.service.service.delete": {
"granted": {
"anyOf": [{ "backend": ["team.team.service.manager","team.service.service.manager"] }]
"anyOf": [{ "backend": ["system.workspace.service.manager_all","team.team.service.manager","team.service.service.manager"] }]
}
},
"team.service.policy.view": {
"granted": {
"anyOf": [{ "backend": ["team.service.strategy.view"] }]
}
},
"team.service.policy.add": {
"granted": {
"anyOf": [{ "backend": ["team.service.strategy.manager"] }]
}
},
"team.service.policy.edit": {
"granted": {
"anyOf": [{ "backend": ["team.service.strategy.manager"] }]
}
},
"team.service.policy.publish": {
"granted": {
"anyOf": [{ "backend": ["team.service.strategy.manager"] }]
}
},
"team.service.policy.delete": {
"granted": {
"anyOf": [{ "backend": ["team.service.strategy.manager"] }]
}
},
"team.application.subscription.view": {
"granted": {
"anyOf": [{ "backend": ["team.consumer.subscription.view_subscribed_service"] }]
"anyOf": [{ "backend": ["system.workspace.application.view_all","team.consumer.subscription.view_subscribed_service"] }]
}
},
"team.application.subscription.add": {
"granted": {
"anyOf": [{ "backend": ["team.consumer.subscription.subscribe"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.subscription.subscribe"] }]
}
},
"team.application.subscription.edit": {
"granted": {
"anyOf": [{ "backend": ["team.consumer.subscription.manager_subscribed_services"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.subscription.manager_subscribed_services"] }]
}
},
"team.application.subscription.delete": {
"granted": {
"anyOf": [{ "backend": ["team.team.consumer.subscription.manager_subscribed_services"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.team.consumer.subscription.manager_subscribed_services"] }]
}
},
"team.application.application.view": {
"granted": {
"anyOf": [{ "backend": ["team.team.consumer.view"] }]
"anyOf": [{ "backend": ["system.workspace.application.view_all","team.team.consumer.view"] }]
}
},
"team.application.application.add": {
"granted": {
"anyOf": [{ "backend": ['team.team.consumer.manager',"team.consumer.application.manager"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all",'team.team.consumer.manager',"team.consumer.application.manager"] }]
}
},
"team.application.application.edit": {
"granted": {
"anyOf": [{ "backend": ['team.team.consumer.manager',"team.consumer.application.manager"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all",'team.team.consumer.manager',"team.consumer.application.manager"] }]
}
},
"team.application.application.delete": {
"granted": {
"anyOf": [{ "backend": ['team.team.consumer.manager',"team.consumer.application.manager"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all",'team.team.consumer.manager',"team.consumer.application.manager"] }]
}
},
"team.consumer.authorization.view": {
"granted": {
"anyOf": [{ "backend": ["team.consumer.authorization.view"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all","system.workspace.application.view_all","team.consumer.authorization.view"] }]
}
},
"team.application.authorization.add": {
"granted": {
"anyOf": [{ "backend": ["team.consumer.authorization.manager"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
}
},
"team.application.authorization.edit": {
"granted": {
"anyOf": [{ "backend": ["team.consumer.authorization.manager"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
}
},
"team.application.authorization.delete": {
"granted": {
"anyOf": [{ "backend": ["team.consumer.authorization.manager"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
}
},
"team.application.authorization.cancelSubApply": {
"granted": {
"anyOf": [{ "backend": ["team.consumer.authorization.manager"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
}
},
"team.application.authorization.cancelSub": {
"granted": {
"anyOf": [{ "backend": ["team.consumer.authorization.manager"] }]
"anyOf": [{ "backend": ["system.workspace.application.manager_all","team.consumer.authorization.manager"] }]
}
},
"team.team.team.view": {
"granted": {
"anyOf": [{ "backend": ["team.team.team.view"] }]
"anyOf": [{ "backend": ["system.workspace.team.view_all","team.team.team.view"] }]
}
},
"team.team.team.edit": {
"granted": {
"anyOf": [{ "backend": ["team.team.team.manager"] }]
"anyOf": [{ "backend": ["system.workspace.team.manager","team.team.team.manager"] }]
}
},
"team.team.member.view": {
"granted": {
"anyOf": [{ "backend": ["team.team.member.view"] }]
"anyOf": [{ "backend": ["system.workspace.team.view_all","team.team.member.view"] }]
}
},
"team.team.member.add": {
"granted": {
"anyOf": [{ "backend": ["team.team.member.manager"] }]
"anyOf": [{ "backend": ["system.workspace.team.manager","team.team.member.manager"] }]
}
},
"team.team.member.edit": {
"granted": {
"anyOf": [{ "backend": ["team.team.member.manager"] }]
"anyOf": [{ "backend": ["system.workspace.team.manager","team.team.member.manager"] }]
}
},
"project.mySystem.topology.view": {
@@ -1,204 +0,0 @@
system:
- name: organization
cname: '组织管理'
value: 'organization'
children:
- name: member
cname: '成员'
value: 'member'
access:
- system.settings.account.view
- system.organization.member.add
- system.organization.member.edit
- system.organization.member.delete
- system.organization.member.block
- system.organization.member.department.add
- system.organization.member.department.edit
- system.organization.member.department.delete
- name: team_manager
cname: '团队管理'
desc: '团队管理'
- system.workspace.team.view_all
- system.organization.team.add
- system.organization.team.edit
- system.organization.team.delete
- system.organization.team.running
- name: role_manager
cname: '角色管理'
desc: '角色管理'
- system.organization.role.view
- system.organization.role.system.view
- system.organization.role.system.add
- system.organization.role.system.edit
- system.organization.role.system.delete
- system.organization.role.team.view
- system.organization.role.team.add
- system.organization.role.team.edit
- system.organization.role.team.delete
- name: API Market
cname: 'API市场'
value: 'api_market'
children:
- name: service classification
cname: '服务分类'
value: 'service_classification'
children:
- system.api_market.service_classification.view
- system.api_market.service_classification.add
- system.api_market.service_classification.edit
- system.api_market.service_classification.delete
- name: devops
cname: 运维
value: 'devops'
children:
- name: cluster
cname: 集群
value: 'cluster'
children:
- system.settings.api_gateway.view
- system.devops.cluster.add
- system.devops.cluster.edit
- system.devops.cluster.delete
- name: ssl certificate
cname: 证书
value: 'ssl_certificate'
children:
- system.settings.ssl_certificate.view
- system.devops.ssl_certificate.add
- system.devops.ssl_certificate.edit
- system.devops.ssl_certificate.delete
- name: log configuration
cname: 日志
value: 'log_configuration'
children:
- system.settings.log_configuration.view
- system.devops.log_configuration.add
- system.devops.log_configuration.edit
- system.devops.log_configuration.publish
- system.devops.log_configuration.delete
- name: workspace
cname: 工作空间
value: 'workspace'
children:
- name: application
cname: 应用
value: 'application'
children:
- system.workspace.application.view_all
- name: service
cname: 服务
value: 'service'
children:
- system.workspace.service.view_all
- name: team
cname: 团队
value: 'team'
children:
- system.workspace.team.view_all
- name: api market
cname: API市场
value: 'api_market'
children:
- system.api_portal.api_portal.view
team:
- name: service
cname: 服务
value: 'service'
children:
- name: api
cname: API
value: 'api'
children:
- team.service.api_doc.view
- team.service.api_doc.add
- team.service.api_doc.edit
- name: route
cname: route
value: 'route'
children:
- team.service.router.view
- team.service.router.add
- team.service.router.edit
- team.service.router.delete
- name: upstream
cname: 上游
value: 'upstream'
children:
- team.service.upstream.view
- team.service.upstream.add
- team.service.upstream.edit
- team.service.upstream.delete
- name: release
cname: 发布
value: 'release'
children:
- team.service.release.view
- team.service.release.add
- team.service.release.rollback
- team.service.release.delete
- team.service.release.approval
- team.service.release.online
- team.service.release.cancel
- team.service.release.stop
- name: subscription management
cname: 订阅方管理
value: 'subscription'
children:
- team.service.subscription.view
- team.service.subscription.approval
- team.service.subscription.add
- team.service.subscription.delete
- name: service
cname: 服务管理
value: 'service'
children:
- team.service.service.view
- team.service.service.add
- team.service.service.edit
- team.service.service.delete
- name: application
cname: 应用
value: 'application'
children:
- name: subscription Service
cname: 订阅服务
value: 'subscription'
children:
- team.application.subscription.view
- team.application.subscription.add
- team.application.subscription.edit
- team.application.subscription.delete
- name: authorization
cname: 访问授权
value: 'authorization'
children:
- team.consumer.authorization.view
- team.consumer.authorization.manager
- team.application.authorization.add
- team.application.authorization.edit
- team.application.authorization.delete
- name: application
cname: 应用
value: 'application'
children:
- team.application.application.view
- team.application.application.add
- team.application.application.edit
- team.application.application.delete
- name: team
cname: 团队
value: 'team'
children:
- name: member
cname: 成员
value: 'member'
children:
- team.team.member.view
- team.team.member.add
- team.team.member.edit
- name: team
cname: 团队管理
value: 'team'
children:
- team.team.team.view
- team.team.team.edit
@@ -0,0 +1,88 @@
import { codeBoxLanguagesType } from "@common/components/postcat/api/Codebox";
export const MatchRules = [
{ value: 'inner', label: '数据格式' },
{ value: 'keyword', label: '关键字' },
{ value: 'regex', label: '正则表达式' },
{ value: 'json_path', label: 'JSON Path' }
];
export const DataFormatOptions = [
{ label: '姓名', value: 'name' },
{ label: '手机号', value: 'phone' },
{ label: '身份证号', value: 'id-card' },
{ label: '银行卡号', value: 'bank-card' },
{ label: '日期', value: 'date' },
{ label: '金额', value: 'amount' }
];
export const DataMaskBaseOptionOptions = [
{ value: 'partial-display', label: '局部显示' },
{ value: 'partial-masking', label: '局部遮蔽' },
{ value: 'truncation', label: '截取' },
{ value: 'replacement', label: '替换' },
];
export const DataMaskOrderOptions = [
...DataMaskBaseOptionOptions,
{ label: '乱序', value: 'shuffling' }
]
export const DataMaskReplaceStrOptions = [
{ value: 'random', label: '随机字符串' },
{ value: 'custom', label: '自定义字符串' }
];
export const PolicyOptions = [
{label:'数据脱敏',value:'data-masking'},
]
export const StrategyStatusEnum = {
'update':'待更新',
'online':'已发布',
'offline':'未发布',
"delete":'待删除',
}
export const StrategyStatusColorClass = {
"online":'text-status_success',
"update":'text-status_pending',
"offline":'text-status_fail',
"delete":'text-status_offline',
}
export const contentTypeToLanguageMap: Record<string, codeBoxLanguagesType> = {
// JSON
"application/json": "json",
// XML
"application/xml": "xml",
"text/xml": "xml",
// HTML
"text/html": "html",
// Plain text
"text/plain": "plaintext",
// JavaScript
"application/javascript": "javascript",
"text/javascript": "javascript",
// CSS
"text/css": "css",
// YAML
"application/x-yaml": "yaml",
"text/yaml": "yaml",
// Others (fallback)
"*/*": "plaintext", // 任意类型默认处理为普通文本
};
@@ -0,0 +1,145 @@
import { DefaultOptionType } from "antd/es/select";
import { StrategyStatusEnum } from "./consts";
import { EntityItem } from "../type";
export type DataMaskRuleTableProps = {
disabled?: boolean;
value?: MaskRuleData[];
onChange?: (value: MaskRuleData[]) => void;
}
export type MaskRuleData = {
match: {
type: string;
value: string;
};
mask: {
type: string;
begin?: number;
length?: number;
replace?: {
type: string;
value: string;
};
};
eoKey?: string;
}
export type DataMaskRuleFormProps = {
editData?: MaskRuleData;
ruleList: MaskRuleData[];
onSave: (ruleList: MaskRuleData[]) => void;
onClose: () => void;
modalVisible:boolean
}
export type DataMaskingConfigHandle = {
save: (values: any) => void
}
export type PolicyMatchType = {name:string, values:string[], label?:string, title?:string, type?:string}
export type DataMaskingRulesType = {}
export type DataMaskingConfigFieldType = {
id:string
name:string
priority:number
description:string
filters:PolicyMatchType[]
config:{
rules:DataMaskingRulesType
}
}
export type DataMaskStrategyItem = {
id:string
name:string
priority:number
isStop:boolean
isDelete:boolean
publishStatus:keyof typeof StrategyStatusEnum
filters:string
conf:string
updater:EntityItem
updateTime:string
}
export type DataMaskLogItem = {
id:string
service: {
id:string
name:string
}
method:string
url:string
remote_ip:string
consumer: {
id:string
name:string
}
authorization:string
record_time:string
}
export type FilterFormField= {
name: string;
values:string[] |string;
label:string
title:string
}
export type FilterOptionType = {
name:string
pattern:string
title:string
type:'remote'|'pattern'|'static'
options:string[]
}
export type FilterTableProps = {
disabled?: boolean;
drawerTitle?: string;
value?:FilterFormField[];
onChange?:(val:FilterFormField[])=>void
}
export type FilterFormType = {
name:string
values:unknown
type?:string
}
export type FilterFormProps = {
filterForm: FilterFormType;
filterOptions:DefaultOptionType[];
selectedOptionNameSet: Set<string>;
disabled: boolean;
onFilterFormChange: (form: FilterFormType) => void;
setFormCanSubmit:(canSubmit:boolean)=>void
serviceId?:string
teamId?:string
}
export type FilterFormHandle = {
clear:()=>void
save:()=>Promise<FilterFormField>
}
export type FilterFormItemProps = {
value?: string[];
onChange?: (value: string[]) => void;
disabled:boolean
option:unknown
onShowValueChange?:(value:string)=>void
serviceId?:string
teamId?:string
}
export type RemoteTitleType = {
title:string
field:string
}
+112 -1
View File
@@ -1,5 +1,9 @@
import { FC, ReactElement, ReactNode } from "react"
import { PERMISSION_DEFINITION } from "./permissions"
import { MatchPositionEnum, MatchTypeEnum } from "@core/const/system/const"
import usePluginLoader from "@common/hooks/pluginLoader"
import { useGlobalContext } from "@common/contexts/GlobalStateContext"
import { StrategyStatusEnum } from "./policy/consts"
export type UserInfoType = {
username: string
@@ -8,7 +12,6 @@ export type UserInfoType = {
phone: string
avatar: string
type:string
}
export type UserProfileProps = {
@@ -100,4 +103,112 @@ export type SimpleMemberItem = {
email:string
department:string
avatar:string
}
export type RouteConfig = {
path:string
pathPrefix?:string
component?:ReactElement
children?:(RouteConfig|false)[]
key:string
provider?:FC<{ children: ReactNode; }>
lazy?:unknown
data?:Record<string, string>
lifecycle?:{
canActivate?:()=>Promise<boolean>
canLoad?:()=>Promise<boolean>
canDeactivate?:()=>Promise<boolean>
deactivated?:()=>Promise<boolean>
}
}
export type RouterParams = {
teamId:string
apiId:string
serviceId:string
clusterId:string;
memberGroupId:string
userGroupId:string
pluginName:string
moduleId:string
accessType:'project'|'team'|'service'
categoryId:string
tagId:string
dashboardType:string
dashboardDetailId:string
topologyId:string
appId:string
roleType:string
roleId:string
routeId:string
policyId:string
}
export type PluginRouterConfig = {
name:string
path:string;
type:string;
expose?:string
}
export type CoreObj = {
routerConfig: RouteConfig[];
setExecuteList: (param:unknown[])=>void;
pluginLoader: {
loadModule: (path: string, name: string, expose: string, pluginPath: string) => Promise<any>;
};
pluginProvider: ReturnType<typeof useGlobalContext>
// pluginLifecycleGuard: PluginLifecycleGuard;
builtInPluginLoader: (name: string) => any;
}
export type PluginConfigType = {
name: string;
router: Array<PluginRouterConfig>;
path?: string;
driver:string
}
export type ApiparkPluginDriverType = {
[key:string]:{[key:string]:(coreObj?:CoreObj, pluginConfig?:PluginConfigType)=>(CoreObj|undefined)}
}
export type RouterMapConfig = {
type: 'component' | 'module',
component: ReactElement,
provider?: FC,
lazy?: FC
key?: string
children?: RouteConfig[]
data?:Record<string, string>
pathMatch?:string
}
export type PolichPublishItemType = {
name:string
priority:number
status:keyof typeof StrategyStatusEnum
optTime:string
}
// 发布详情(版本)
export type PolicyPublishInfoType = {
source:string
strategies:Array<PolichPublishItemType>
isPublish:boolean
versionName:string
unpublishMsg:string
};
export type PolicyPublishModalProps = {
data:PolicyPublishInfoType
}
export type PolicyPublishModalHandle = {
publish:()=>Promise<boolean|string|Record<string, unknown>>
}
@@ -1,11 +1,19 @@
import {createContext, Dispatch, FC, ReactNode, useContext, useReducer, useState} from "react";
import {createContext, Dispatch, FC, ReactNode, useContext, useEffect, useReducer, useState} from "react";
import { useFetch } from "@common/hooks/http";
import { App } from "antd";
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
import { checkAccess } from "@common/utils/permission";
import { PERMISSION_DEFINITION } from "@common/const/permissions";
import { $t } from "@common/locales";
import { MenuItem } from "@common/utils/navigation";
import { ErrorBoundary } from "@ant-design/pro-components";
import NotFound from "@common/components/aoplatform/NotFound";
import { RouteConfig } from "@common/const/type";
import { ProtectedRoute } from "@core/components/aoplatform/RenderRoutes";
import Login from "@core/pages/Login";
import { useLocaleContext } from "./LocaleContext";
import Root from "@core/pages/Root"
import DataMaskingCompare from "@core/pages/policy/dataMasking/DataMaskingCompare";
interface GlobalState {
isAuthenticated: boolean;
userData: UserData | null;
@@ -14,6 +22,7 @@ interface GlobalState {
powered:string;
mainPage:string;
language:string;
pluginsLoaded:boolean
}
interface UserData {
@@ -29,8 +38,171 @@ export type GlobalAction =
| { type: 'UPDATE_POWER'; powered: string }
| { type: 'UPDATE_MAIN_PAGE'; mainPage: string }
| { type: 'UPDATE_LANGUAGE'; language: string }
| { type: 'SET_PLUGINS_LOADED'; pluginsLoaded: boolean }
const mockData = [
{
"name": "工作空间",
"key": "workspace",
"path": "/guide",
"icon": "ic:baseline-space-dashboard",
"children": [
{
"name": "首页",
"key": "guide",
"path": "/guide",
"icon": "ic:baseline-home",
"access": "all"
},
{
"name": "服务",
"key": "service",
"path": "/service",
"icon": "ic:baseline-blinds-closed",
"access": "all"
},
{
"name": "消费者",
"key": "consumer",
"path": "/consumer",
"icon": "ic:baseline-apps",
"access": "all"
},
{
"name": "团队",
"key": "team",
"path": "/team",
"icon": "ic:baseline-people-alt",
"access": "all"
},
// {
// "name": "路由组件",
// "key": "router",
// "path": "/router1",
// "icon": "ic:baseline-people-alt",
// "access": "all"
// }
]
},
{
"name": "API 市场",
"key": "serviceHub",
"path": "/serviceHub",
"icon": "ic:baseline-hub",
"access": "system.api_portal.api_portal.view"
},
{
"name": "仪表盘",
"key": "analytics",
"path": "/analytics",
"icon": "ic:baseline-bar-chart",
"children": [
{
"name": "运行视图",
"key": "analytics",
"path": "/analytics",
"icon": "ic:baseline-bar-chart",
"access": "system.analysis.run_view.view"
}
],
"access": "system.analysis.run_view.view"
},
{
"name": "系统设置",
"key": "operationCenter",
"path": "/commonsetting",
"icon": "ic:baseline-settings",
"children": [
{
"name": "系统",
"key": "serviceHubSetting",
"path": "/commonsetting",
"children": [
{
"name": "常规",
"key": "commonsetting",
"path": "/commonsetting",
"icon": "ic:baseline-hub",
"access": "system.api_market.service_classification.view"
},
{
"name": "API 网关",
"key": "cluster",
"path": "/cluster",
"icon": "ic:baseline-device-hub",
"access": "system.settings.api_gateway.view"
},
{
"name": "AI 模型",
"key": "aisetting",
"path": "/aisetting",
"icon": "hugeicons:ai-network",
"access": "system.settings.ai_provider.view"
}
],
},
{
"name": "用户",
"key": "organization",
"path": "/member",
"children": [
{
"name": "账号",
"key": "member",
"path": "/member",
"icon": "ic:baseline-people-alt",
"access": "system.settings.account.view"
},
{
"name": "角色",
"key": "role",
"path": "/role",
"icon": "ic:baseline-verified-user",
"access": "system.organization.role.view"
}
]
},
{
"name": "集成",
"key": "maintenanceCenter",
"path": "/datasourcing",
"children": [
{
"name": "数据源",
"key": "datasourcing",
"path": "/datasourcing",
"icon": "ic:baseline-monitor-heart",
"access": "system.settings.data_source.view"
},
{
"name": "全局策略",
"key": "globalpolicy",
"path": "/globalpolicy",
"icon": "icon-park-solid:exchange-three",
"access": "system.settings.data_source.view"
},
{
"name": "证书",
"key": "cert",
"path": "/cert",
"icon": "ic:baseline-security",
"access": "system.settings.ssl_certificate.view"
},
{
"name": "日志",
"key": "logsettings",
"path": "/logsettings",
"icon": "ic:baseline-sticky-note-2",
"access": "system.settings.log_configuration.view"
},
]
}
]
}
]
/*
*/
@@ -39,9 +211,11 @@ export const GlobalContext = createContext<{
dispatch: Dispatch<GlobalAction>;
accessData:Map<string,string[]>;
pluginAccessDictionary:{[k:string]:string};
menuList:MenuItem[];
getGlobalAccessData:()=>Promise<{ access:string[]}>;
getTeamAccessData:(teamId:string)=>void;
getPluginAccessDictionary:(pluginData:{[k:string]:string})=>void
getMenuList:()=>void
resetAccess:()=>void
cleanTeamAccessData:()=>void
checkPermission:(access:keyof typeof PERMISSION_DEFINITION[0] | Array<keyof typeof PERMISSION_DEFINITION[0]>)=>boolean
@@ -49,6 +223,11 @@ export const GlobalContext = createContext<{
accessInit:boolean
aiConfigFlushed:boolean
setAiConfigFlushed:(flush:boolean)=>void
routeConfig: RouteConfig[];
setRouterConfig: (isRoot: boolean, config: RouteConfig) => void;
addRouteConfig: (parentRoute: RouteConfig, config: RouteConfig) => void;
fetchData: ReturnType<typeof useFetch>['fetchData'];
$t: typeof $t;
} | undefined>(undefined);
const globalReducer = (state: GlobalState, action: GlobalAction): GlobalState => {
@@ -94,23 +273,38 @@ const globalReducer = (state: GlobalState, action: GlobalAction): GlobalState =>
...state,
language: action.language,
};
case 'SET_PLUGINS_LOADED':
return {
...state,
pluginsLoaded: action.pluginsLoaded,
};
default:
return state;
}
};
export const DefaultRouteConfig = [
{ path: '/', pathMatch: 'full', component: <Root /> ,key:'root',},
{ path: '/login', component: <Login /> ,key:'login'},
{ path: '/dataMaskCompare/:logId/:serviceId?/:teamId?', component: <DataMaskingCompare /> ,key:'dataMaskCompare'},
{ path: '/', pathMatch:'prefix',component:<ProtectedRoute /> ,key:'basciLayout',children:[
{ path: '*', component: <ErrorBoundary><NotFound/></ErrorBoundary>, key: 'errorBoundary' }
]}
]
// Create a context provider component
export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
const {fetchData} = useFetch()
const { message } = App.useApp()
const { setLocale } = useLocaleContext();
const [state, dispatch] = useReducer(globalReducer, {
isAuthenticated: true, //mock用
isAuthenticated: false, //mock用
userData: null,
version: '1.0.0',
updateDate: '2024-07-01',
powered:'Powered by https://apipark.com',
mainPage:'/guide/page',
language:'en-US'
language:'en-US',
pluginsLoaded:false
});
const [accessData,setAccessData] = useState<Map<string,string[]>>(new Map())
const [pluginAccessDictionary, setPluginAccessDictionary] = useState<{[k:string]:string}>({})
@@ -118,6 +312,43 @@ export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
const [accessInit, setAccessInit] = useState<boolean>(false)
const [aiConfigFlushed, setAiConfigFlushed] = useState<boolean>(false)
let getGlobalAccessPromise: Promise<BasicResponse<{ access:string[] }>> | null = null
const [menuList, setMenuList] = useState<MenuItem[]>(mockData);
const [routeConfig, setRouteConfigState] = useState<RouteConfig[]>(DefaultRouteConfig)
useEffect(() => {
setLocale(state.language);
}, [state.language, setLocale]);
const { fetchData } = useFetch();
const setRouterConfig = (isRoot: boolean, config: RouteConfig) => {
setRouteConfigState(prevConfig => {
if (isRoot) {
return [config,...prevConfig];
} else {
const rootRoute = prevConfig.find(route => route.path === '/' && route?.pathMatch === 'prefix') ;
if (rootRoute ) {
rootRoute.children = rootRoute.children ? [config, ...rootRoute.children] : [config];
}
return [...prevConfig];
}
});
};
const addRouteConfig = (parentRoute: RouteConfig, config: RouteConfig) => {
const addConfigToParent = (routes: RouteConfig[]): RouteConfig[] => {
return routes.map(route => {
if (route.key === parentRoute.key) {
route.children = route.children ? [...route.children, config] : [config];
} else if (route.children) {
route.children = addConfigToParent(route.children);
}
return route;
});
};
setRouteConfigState(prevConfig => addConfigToParent(prevConfig));
};
const getGlobalAccessData = ()=>{
if(getGlobalAccessPromise){
@@ -151,6 +382,18 @@ export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
})
}
const getMenuList = ()=>{
// TODO 等待对接后端接口
// fetchData<BasicResponse<{ access:string[]}>>('profile/permission/team',{method:'GET',eoParams:{team:teamId}},).then(response=>{
// const {code,data,msg} = response
// if(code === STATUS_CODE.SUCCESS){
// setMenuList(data.menus)
// }else{
// message.error(msg || $t(RESPONSE_TIPS.error))
// }
// })
}
const cleanTeamAccessData = ()=>{
setTeamDataFlushed(false)
setAccessData(prevData => prevData.set('team',[]))
@@ -176,15 +419,26 @@ export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
return revs
}
return (
<GlobalContext.Provider value={
{ state, dispatch,accessData,pluginAccessDictionary,
{ state, dispatch,
accessData,
pluginAccessDictionary,
getGlobalAccessData,
getPluginAccessDictionary,
getTeamAccessData,teamDataFlushed,
getTeamAccessData,teamDataFlushed,getMenuList,menuList,
cleanTeamAccessData,
resetAccess ,checkPermission,accessInit,
aiConfigFlushed, setAiConfigFlushed}}>
resetAccess ,checkPermission,
accessInit,
aiConfigFlushed,
setAiConfigFlushed,
routeConfig,
setRouterConfig,
addRouteConfig,
fetchData,
$t:$t,}}>
{children}
</GlobalContext.Provider>
);
@@ -194,7 +448,40 @@ export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
export const useGlobalContext = () => {
const context = useContext(GlobalContext);
if (!context) {
throw new Error('useGlobalContext must be used within a GlobalProvider');
console.warn('useGlobalContext must be used within a GlobalProvider. Returning default context.');
return {
state: {
isAuthenticated: false,
userData: null,
version: '1.0.0',
updateDate: '',
powered: '',
mainPage: '',
language: 'en-US',
pluginsLoaded: false,
},
dispatch: () => {},
accessData: new Map(),
pluginAccessDictionary: {},
menuList: [],
getGlobalAccessData: async () => ({ access: [] }),
getTeamAccessData: () => {},
getPluginAccessDictionary: () => {},
getMenuList: () => {},
resetAccess: () => {},
cleanTeamAccessData: () => {},
checkPermission: () => false,
teamDataFlushed: false,
accessInit: false,
aiConfigFlushed: false,
setAiConfigFlushed: () => {},
routeConfig: [],
setRouterConfig: () => {},
addRouteConfig: () => {},
fetchData: async () => ({}),
$t: (key: string) => key,
};
}
return context;
};
@@ -0,0 +1,47 @@
import React, { createContext, useContext, useState, useEffect, useMemo, FC, ReactNode } from 'react';
import { ConfigProviderProps } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import enUS from 'antd/locale/en_US';
import zhTW from 'antd/locale/zh_TW';
import jaJP from 'antd/locale/ja_JP';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import 'dayjs/locale/zh-tw';
import 'dayjs/locale/ja';
type Locale = ConfigProviderProps['locale'];
const languageMap: Record<string, Locale> = {
'zh-CN': zhCN,
'en-US': enUS,
'zh-TW': zhTW,
'ja-JP': jaJP,
};
const LocaleContext = createContext<{
locale: Locale;
setLocale: (locale: string) => void;
}|undefined>(undefined);
export const LocaleProvider: FC<{children:ReactNode}> = ({ children }) => {
const [locale, setLocaleState] = useState<Locale>(zhCN);
const setLocale = (language: string) => {
dayjs.locale(language);
setLocaleState(languageMap[language]);
};
return (
<LocaleContext.Provider value={{ locale, setLocale }}>
{children}
</LocaleContext.Provider>
);
};
export const useLocaleContext = () => {
const context = useContext(LocaleContext);
if (!context) {
throw new Error('useLocaleContext must be used within a LocaleContext');
}
return context;
};
@@ -0,0 +1,76 @@
import { createContext, FC, ReactNode, useContext, useState } from "react";
class EventEmitter {
// 用来存放注册的事件与回调
_events:any
constructor () {
this._events = {}
}
on (eventName:string, callback:Function) {
// 由于一个事件可能注册多个回调函数,所以使用数组来存储事件队列
const callbacks = this._events[eventName] || []
callbacks.push(callback)
this._events[eventName] = callbacks
}
// 此处需要处理,emit时需要按顺序执行监听的函数,每个函数都会返回是否中止的参数,如果中止则不执行后续的函数
// emit传入eventName 和 event, 返回 event
emit (eventName:string, event:any) {
return new Promise((resolve) => {
const callbacks = this._events[eventName] || []
for (const cb of callbacks) {
const cbRes = cb(event.data)
if (cbRes.continue === false) {
resolve(cbRes)
break
} else {
event = cbRes
}
}
resolve(event.data)
})
}
// 取消订阅
off (eventName:string, callback:Function) {
const callbacks = this._events[eventName] || []
const newCallbacks = callbacks.filter((fn:any) => fn !== callback && fn.initialCallback !== callback /* 用于once的取消订阅 */)
this._events[eventName] = newCallbacks
}
// 单次订阅,后台插件可以自行决定取消对事件的订阅
once (eventName:string, callback:Function) {
// 由于需要在回调函数执行后,取消订阅当前事件,所以需要对传入的回调函数做一层包装,然后绑定包装后的函数
const one = (...args:any) => {
callback(...args)
this.off(eventName, one)
}
// 由于:我们订阅事件的时候,修改了原回调函数的引用,所以,用户触发 off 的时候不能找到对应的回调函数
// 所以,我们需要在当前函数与用户传入的回调函数做一个绑定,我们通过自定义属性来实现
one.initialCallback = callback
this.on(eventName, one)
}
}
export const PluginEventHubContext = createContext<EventEmitter | undefined>(undefined);
export const PluginEventHubProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [pluginEventHub] = useState<EventEmitter>(new EventEmitter());
return (
<PluginEventHubContext.Provider value={pluginEventHub}>
{children}
</PluginEventHubContext.Provider>
);
};
export const usePluginEventHub = () => {
const context = useContext(PluginEventHubContext);
if (!context) {
throw new Error('usePluginEventHub must be used within a PluginEventHubProvider');
}
return context;
};
@@ -0,0 +1,36 @@
import { createContext, FC, ReactNode, useContext, useState } from "react";
export const PluginSlotHubContext = createContext<{
addSlot: (name: string, content: unknown) => void;
addSlotArr: (name: string, content: unknown[]) => void;
removeSlot: (name: string) => void;
getSlot: (name: string) => unknown;
} | undefined>(undefined);
export const PluginSlotHubProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [pluginSlotHub] = useState<Map<string, unknown>>(new Map());
const pluginSlotHubService = {
addSlot: (name: string, content: any) => {
pluginSlotHub.set(name, pluginSlotHub.get(name) ? [...(pluginSlotHub.get(name) as Array<unknown>), content] : [content] ); },
addSlotArr: (name: string, content: any[]) => { pluginSlotHub.get(name) ? pluginSlotHub.set(name, (pluginSlotHub.get(name) as Array<unknown>).push(content)) : pluginSlotHub.set(name, content); },
removeSlot: (name: string) => { pluginSlotHub.delete(name); },
getSlot: (name: string) => {
return pluginSlotHub.get(name) ; }
};
return (
<PluginSlotHubContext.Provider value={pluginSlotHubService}>
{children}
</PluginSlotHubContext.Provider>
);
};
export const usePluginSlotHub = () => {
const context = useContext(PluginSlotHubContext);
if (!context) {
throw new Error('usePluginSlotHub must be used within a PluginSlotHubProvider');
}
return context;
};
@@ -1,6 +1,7 @@
import { RESPONSE_TIPS } from '@common/const/const';
import { message } from 'antd';
import { $t } from "@common/locales"
import { useEffect, useState } from 'react';
const useCopyToClipboard = () => {
+10 -3
View File
@@ -1,4 +1,6 @@
import { STATUS_CODE } from "@common/const/const";
import { BasicResponse, STATUS_CODE } from "@common/const/const";
import { usePluginEventHub } from "@common/contexts/PluginEventHubContext";
import EventEmitter from "events";
const urlWhiteList = [/api.example.com\/users/, /api.example2.com\/products/]; // 正则白名单
@@ -137,7 +139,10 @@ type EoRequest = RequestInit & {eoParams?:{[k:string]:unknown},eoTransformKeys?:
type EoHeaders = Headers | {[k:string]:string}
export function useFetch(){
export function useFetch() {
// plugin cannot use usePluginEventHub directly, so we need to pass it as a parameter
const pluginEventHub = usePluginEventHub()
function fetchData<T>(url:string, options: EoRequest ) {
// 合并传入的headers与默认headers
const headers = { ...(options.body ? {}:DEFAULT_HEADERS), ...options.headers };
@@ -184,9 +189,11 @@ export function useFetch(){
// 如果响应体为JSON且指定了转换键,则转换响应数据
if ( isJsonHttp(response.headers)) {
const data = await response.json();
return shouldTransformKeys ? keysToCamel(data,options.eoTransformKeys as string[]) as T:data
const newData = await pluginEventHub.emit('httpResponse', {data,continue:true}) as Response;
return shouldTransformKeys ? keysToCamel(newData,options.eoTransformKeys as string[]) as T:data
}
return response;
})
.catch(error => {
@@ -0,0 +1,449 @@
import { useEffect, useState } from "react";
import { useGlobalContext } from "@common/contexts/GlobalStateContext";
import { DEFAULT_LOCAL_PLUGIN_PATH, generateRemoteModuleTemplate, loadRemoteModule, validateExportLifecycle } from "@common/utils/plugin.tsx";
import { useFetch } from "@common/hooks/http";
import { PluginConfigType, RouteConfig } from "@common/const/type.ts";
import { ApiparkPluginDriverType ,RouterMapConfig} from "@common/const/type";
import { usePluginEventHub } from "@common/contexts/PluginEventHubContext";
import { usePluginSlotHub } from "@common/contexts/PluginSlotHubContext";
import { App } from "antd";
const mockData = {
buildAt:'2024-09-13T03:51:25Z',
build_user:'gitlab-runner',
git_commint:'6438d5aaff146dc560ed0d8563788e64a49640a5',
goversion:'go version go1.21.4 linux/amd64',
guide:true,
plugins:[
{
driver:'apipark.builtIn.component',
name:'guide',
router:[
{
path:'guide/*',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'team',
router:[
{
path:'team',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'service',
router:[
{
path:'service',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'datasourcing',
router:[
{
path:'datasourcing',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'cluster',
router:[
{
path:'cluster',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'aisetting',
router:[
{
path:'aisetting',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'cert',
router:[
{
path:'cert',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'serviceHub',
router:[
{
path:'serviceHub',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'commonsetting',
router:[
{
path:'commonsetting',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'consumer',
router:[
{
path:'consumer',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'member',
router:[
{
path:'member',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'role',
router:[
{
path:'role',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'analytics',
router:[
{
path:'analytics',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'template',
router:[
{
path:'template/:moduleId',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'logsettings',
router:[
{
path:'logsettings/*',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'resourcesettings',
router:[
{
path:'resourcesettings/*',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'userProfile',
router:[
{
path:'userProfile',
type:'normal'
}
]
},
{
driver:'apipark.builtIn.component',
name:'globalPolicy',
router:[
{
path:'globalPolicy',
type:'normal'
}
]
},
// {
// "driver": "apipark.remote.normal",
// "name": "remote",
// "router": [
// {
// "path": "remote",
// "type": "normal"
// }
// ]
// },
// {
// "driver": "apipark.local.preload",
// "name": "auth",
// "router": [
// {
// "expose": "AppModule",
// "path": "auth",
// "type": "root"
// },
// {
// "expose": "AuthInfoModule",
// "path": "auth-info",
// "type": "normal"
// }
// ]
// },
// {
// "driver": "apipark.builtIn.component",
// "name": "email",
// "router": [
// {
// "path": "system/email",
// "type": "normal"
// }
// ]
// },
// {
// "driver": "apipark.builtIn.module",
// "name": "open-api",
// "router": [
// {
// "path": "system/ext-app",
// "type": "normal"
// }
// ]
// },
// {
// "driver": "apipark.local.preload",
// "name": "remote",
// "router": [
// {
// "expose": "App",
// "path": "router1/*",
// "type": "normal"
// }
// ]
// },
// {
// "driver": "apipark.remote.normal",
// "name": "apispace",
// "router": [
// {
// "path": "remote/apispace",
// "type": "normal"
// }
// ]
// }
],
powered:'Powered by https://eolink.com',
product:'apipark',
version:'6438d5aa'
}
export type ExecutePluginType = PluginConfigType & {
expose:string,
bootstrap:string
}
const usePluginLoader = (apipark:ApiparkPluginDriverType,routerMap:Map<string, RouterMapConfig>) => {
const [modules, setModules] = useState(new Map());
const [executeList, setExecuteList] = useState<ExecutePluginType[]>([]);
const [baseHref, setBaseHref] = useState('');
const [pendingTasks, setPendingTasks] = useState(0);
const {fetchData} = useFetch();
const pluginProvider = useGlobalContext();
const pluginEventHub = usePluginEventHub();
const pluginSlotHub = usePluginSlotHub();
const { getMenuList,dispatch} = pluginProvider
const { modal,message } = App.useApp()
const [startLoadExecutePlugin, setStartLoadExecutePlugin] = useState<boolean>(false)
const messageService = message;
const modalService = modal;
let startInstallPlugin = false
useEffect(()=>{
if (startLoadExecutePlugin && pendingTasks === 0 && executeList.length > 0) {
loadExecutedPlugin();
}
},[pendingTasks, executeList])
const getModule = (routerPrefix:string, specific = false) => {
if (routerPrefix.startsWith('/')) {
routerPrefix = routerPrefix.substring(1);
}
if (specific) {
return modules.get(routerPrefix);
}
let matchedModule = null;
let matchedLength = 0;
modules.forEach((value, key) => {
if (routerPrefix.startsWith(key) && key.length > matchedLength) {
matchedModule = value;
matchedLength = key.length;
}
});
return matchedModule;
};
const loadModule = async (routerPrefix: string, pluginName: any, exposedModule: string , pluginPath: any) => {
if (!modules.get(routerPrefix)) {
try {
const loadedModule = await loadRemoteModule(generateRemoteModuleTemplate(pluginName, exposedModule, pluginPath));
const Module = loadedModule.default ?? loadedModule
let ModuleBootstrap;
try {
ModuleBootstrap = await loadRemoteModule(generateRemoteModuleTemplate(pluginName, 'Bootstrap', pluginPath));
} catch (error) {
console.warn('Bootstrap module not found:', error);
}
setModules(prevModules => new Map(prevModules).set(routerPrefix,Module[exposedModule] ));
if (!validateExportLifecycle(Module)) {
console.error('需要导出插件生命周期函数');
return;
}
await Module.bootstrap?.({
pluginProvider,
pluginEventHub,
pluginSlotHub
});
return Module;
} catch (error) {
console.error('导入插件失败:', error);
}
}
return getModule(routerPrefix, true);
};
const loadExecutedPlugin = async () => {
setStartLoadExecutePlugin(true)
for (const plugin of executeList) {
try {
const Module = await loadRemoteModule(generateRemoteModuleTemplate(plugin.name, plugin?.expose || 'Bootstrap', plugin.path || `${DEFAULT_LOCAL_PLUGIN_PATH}${plugin.name}/apipark.js`));
const bootstrap = Module.bootstrap;
if (!bootstrap) {
console.warn('立即执行插件未导出Bootstrap模块或bootstrap函数');
} else {
await bootstrap({
pluginEventHub,
pluginSlotHub,
pluginProvider,
platformProvider:null,
messageService,
modalService,
});
}
} catch (error) {
console.error('执行插件失败:', error);
}
}
};
const loadPlugins = () => {
return new Promise((resolve) => {
if(startInstallPlugin) {
return resolve(true)
}
startInstallPlugin = true
installPlugin().then(async (res)=>{
// reset route after loading executed plugins
await loadExecutedPlugin();
return resolve(res)
})
});
};
const installPlugin = () => {
return new Promise((resolve, reject) => {
// fetchData('system/plugins',{method:'GET'}).then((resp) => {
// if (resp.code === 0){
const resp = {data:mockData}
dispatch({type:'UPDATE_VERSION',version:resp.data.version})
dispatch({type:'UPDATE_DATE',updateDate:resp.data.buildAt})
dispatch({type:'UPDATE_POWER',powered:resp.data.powered})
const driverMethod = { apipark: apipark };
const pluginConfigList = resp.data.plugins;
const pluginLoader = { loadModule };
const pluginLifecycleGuard ={};
const builtInPluginLoader = loadBuiltInModule;
pluginSlotHub.addSlot('renewMenu', () => {
getMenuList()
});
for (const plugin of pluginConfigList) {
try {
const driverName = plugin.driver;
if (!driverName) {
console.error('no driver name');
continue;
}
const driver = driverName.split('.').reduce((driverMethod: { [x: string]: any; }, driverName: string | number) => driverMethod[driverName], driverMethod);
if(driverName.split('.')[2] === 'preload'){
setPendingTasks(prev => prev + 1);
}
;(driver as Function )?.({ setExecuteList:(callback:ExecutePluginType[]) => {
setExecuteList(callback);
setPendingTasks(prev => prev - 1);
}, pluginLoader, pluginProvider, pluginLifecycleGuard, builtInPluginLoader }, plugin);
} catch (err) {
console.warn('安装插件出错:', err);
}
}
resolve(true);
// } else {
// messageService.error(resp.msg || '获取插件配置列表失败,请重试!');
// reject(new Error(resp.msg || '获取插件配置列表失败'));
// }
// });
}
);
};
const loadBuiltInModule = (pluginName: any) => {
try {
const { module } = routerMap.get(pluginName)!;
return module;
} catch (err) {
console.warn(`安装内置插件[${pluginName}]出错:`, err);
}
};
return {
loadPlugins,
loadModule,
loadExecutedPlugin,
setBaseHref,
getModule
};
};
export default usePluginLoader;
@@ -1,31 +1,8 @@
{
"工作空间": "Kc0e5ef9f",
"首页": "K4de11e23",
"服务": "Kb58e0c3f",
"消费者": "K7acfcfad",
"团队": "Kc9e489f5",
"API 市场": "K61c89f5f",
"仪表盘": "K16d71239",
"运行视图": "K714c192d",
"系统拓扑图": "Kd57dfe97",
"系统设置": "K3fe97dcc",
"系统": "Kecbb0e45",
"常规": "Ka358e23d",
"API 网关": "K449058e9",
"AI 模型": "K99935e6f",
"用户": "K1deaa2dd",
"账号": "K80a560a1",
"角色": "Kf644225f",
"集成": "K4057391a",
"数据源": "K8fa58214",
"证书": "K481e8a05",
"日志": "Kca53edd0",
"资源": "Kb283e720",
"Open API": "K631d646f",
"账号设置": "K6535ff9c",
"退出登录": "Kf15499b4",
"文档": "Kabbd6e6",
"APIPark - 企业API数据开放平台": "K1196b104",
"APIPark": "K630c9e6d",
"HTTP 状态码": "K1f42de3",
"系统状态码": "K4770dff4",
"描述": "Kf89e58f1",
@@ -43,6 +20,8 @@
"ID": "K11d3633a",
"名称": "Kbff43de3",
"Driver": "K16ca79ef",
"资源": "Kb283e720",
"日志": "Kca53edd0",
"已发布": "K7a369eef",
"下线": "Kcfa1a4d2",
"上线": "K771dc3b7",
@@ -50,6 +29,8 @@
"删除": "Kecbd7449",
"确认": "K1cbe2507",
"搜索(0)名称": "K48325b6",
"发布名称": "Ka3e9f580",
"策略列表": "Kb2480682",
"上下文": "Kc6340091",
"查询内容": "K74ecb1fa",
"会话历史": "K79f2e2f9",
@@ -64,6 +45,8 @@
"成功": "K43fcaf94",
"上线失败": "Kc71c6a9",
"失败": "K56c686f8",
"正常": "Ke039b9b5",
"无效": "K1da86266",
"申请系统": "K1ff96ff",
"所属团队": "K9bf855d6",
"申请人": "K11b994ed",
@@ -234,6 +217,28 @@
"地址": "K78b1ca25",
"新增": "K1644b775",
"申请方消费者": "Kec91f0db",
"策略名称": "K931615d7",
"优先级": "K31faa2a1",
"筛选条件": "Kbdec9fa",
"处理数": "Kbcbb7391",
"数据格式": "K118d8d74",
"关键字": "Kfe7c7d2d",
"正则表达式": "K2f57a694",
"手机号": "K8953e0a6",
"身份证号": "K6f86a038",
"银行卡号": "K7954e7c8",
"金额": "K320fdb17",
"日期": "K7867acda",
"局部显示": "K7d327ae8",
"局部遮蔽": "Kfbf38e3c",
"截取": "Kd8c1fbb0",
"替换": "K89829921",
"乱序": "K480a7165",
"随机字符串": "Kea0d69df",
"自定义字符串": "Ke7c84d1d",
"请输入IP地址或CIDR范围,每条以换行分割": "K49731763",
"待更新": "K3a34d49b",
"待删除": "Kd2850420",
"暂无操作权限,请联系管理员分配。": "K23fda291",
"微信小程序": "K4618cb0a",
"获取文件,需填路径": "Ka854f511",
@@ -321,14 +326,18 @@
"至": "K7e1ab4b0",
"详情": "Kf1b166e7",
"暂不支持带有双斜杠//的url": "K28555332",
"输入的IP或CIDR不符合格式": "K83237c89",
"请正确输入路径,如/usr/*或*/usr/*": "K5ae2c87a",
"必填项": "K71661ee8",
"不是有效邮箱地址": "Kcbee3f8",
"最近一次更新者": "K617f34f1",
"最近一次更新时间": "K6ebca204",
"保存": "Kabfe9512",
"服务": "Kb58e0c3f",
"API 路由": "K51d1eb5d",
"API 文档": "Ka2b6d281",
"使用说明": "Kdefa9caa",
"服务策略": "K52f72551",
"发布": "K36856e71",
"订阅管理": "K6382bbfd",
"订阅审核": "K31af5b99",
@@ -380,6 +389,8 @@
"发布申请": "K56b4254f",
"API 调用地址": "Kea2f9279",
"API base URL 一般设置为API 网关的外部网络访问地址,或者是API网关绑定的域名。": "K7fc496a1",
"集成地址": "K508d8bf4",
"与外部平台集成时,获取 API 市场中文档信息的域名": "K67f4e9bb",
"常规设置": "K8ab0fc95",
"API 请求设置": "Kb66fec9d",
"服务分类": "K4de0af74",
@@ -401,11 +412,13 @@
"现在你可以通过 Token 来调用这些 API。": "Kd6d7ca1f",
"快速接入 REST API": "K86cf95f",
"创建 REST 服务和 API": "K7a3a8417",
"仪表盘": "K16d71239",
"统计 API 调用情况": "K4a84214e",
"仪表盘中提供了多种统计图表,帮助我们了解 API 的运行情况。": "K297d8563",
"核心功能": "K2cdbb773",
"账号与角色": "K3378c50d",
"邀请你的团队成员加入 APIPark,共同管理和调用 API。": "Kda5bb930",
"团队": "Kc9e489f5",
"团队中包含了人员、消费者和服务,不同团队之间的消费者和服务数据是隔离的,可用于管理企业内部不同的部门/项目组/团队。": "Keee27105",
"服务内包含一组 API,并且可以发布到 API 市场被其他团队使用。": "Kd5be0cd7",
"权限管理": "K62e89ee7",
@@ -413,6 +426,7 @@
"如果需要调用某个服务的 API,需要先订阅该服务,并且等待提供服务的团队审核后才可发起 API 请求。": "Kf2410413",
"审核订阅申请": "K6c2e44b8",
"提供服务的团队可以审核来自其他团队的订阅申请,审核通过后的消费者才可发起 API 请求。": "Kaa717866",
"集成": "K4057391a",
"APIPark 提供详尽的 API 调用日志,帮助企业监控、分析和审计 API 的运行状况。": "K3453272",
"Hello!欢迎使用 APIPark": "Kd518ba3e",
"你能通过 APIPark 快速在企业内部构建 API 开放门户/市场,享受极致的转发性能、API 可观测、服务治理、多租户管理、订阅审核流程等诸多好处。": "K7e04ea16",
@@ -424,6 +438,7 @@
"了解更多功能": "K48f7e21f",
"隐藏该教程": "K698296e2",
"请输入账号": "Kf076f63c",
"账号": "K80a560a1",
"请输入密码": "K25c895d5",
"密码": "K551b0348",
"登录": "Kd2c1a316",
@@ -455,7 +470,6 @@
"添加部门": "K26c698bb",
"添加子部门": "Kb9cf2a7d",
"重命名": "Kc83551f5",
"该数据删除后将无法找回,请确认是否删除?": "K5cfdd950",
"成员": "K74aef1ad",
"设置成员和对应的角色,成员只能够看到权限范围内的功能和数据。": "K3f1077c9",
"搜索部门": "Kdce62a6",
@@ -466,6 +480,7 @@
"密钥": "K8ef69ee2",
"上传密钥": "Kba3507d6",
"密钥文件的后缀名一般为 .key 的文件内容": "K93ac0f23",
"证书": "K481e8a05",
"上传证书": "K7cdd1331",
"证书文件的后缀名一般为 .crt 或 .pem 的文件内容": "K6d91905d",
"添加证书": "Kd0f6ded7",
@@ -475,7 +490,6 @@
"集群": "Ke93d36ed",
"修改配置": "K877985b7",
"设置访问 API 的集群,让 API 在分布式环境中稳定运行,并且能够根据业务需求进行灵活扩展和优化。": "Kdf66a675",
"正常": "Ke039b9b5",
"异常": "K23a3bd72",
"私有网络": "Ke1b1865",
"公共网络": "K4786c57c",
@@ -484,11 +498,39 @@
"同步地址": "K2a49373f",
"集群地址": "K5878440c",
"下一步": "K5e9022f8",
"数据源": "K8fa58214",
"设置监控报表的数据来源,设置完成之后即可获得详细的API调用统计图表。": "Kdbafd6f9",
"统计图表": "K1358acf",
"地址(IP:端口)": "K62dabdf6",
"组织(Organization": "K2db12335",
"添加策略": "K34d0d409",
"输入名称、筛选条件查找": "Kbb4298ac",
"编辑策略": "Kc82b8374",
"策略类型": "K4b34a5e5",
"匹配条件": "K57f0fee8",
"数据脱敏规则": "K10650c58",
"配置脱敏规则": "K1b34a9ab",
"匹配值": "K26d22405",
"脱敏类型": "K1546e1fe",
"起始位置": "K9b9b0629",
"长度": "K52c84fe1",
"替换类型": "Kde84409c",
"替换值": "K338653b4",
"JSON Path": "Kbaeed3b7",
"脱敏规则": "K4cd91d61",
"自定义字符串; 值:": "K8dcad979",
"起始位置:(0)位;长度:(1)位": "K82e3f7b7",
"编辑": "Kad207008",
"已选择(0)项(1)数据": "K49dfc123",
"所有(0)": "K8457ea34",
"属性名称": "K7ca9a795",
"属性值": "Kc4391744",
"配置(0)": "K678e13fc",
"数据脱敏": "Kabac9caf",
"全局策略": "Ke8cbb878",
"支持对系统全局进行统一的策略配置,从而简化管理并确保一致性。全局策略的优先级比服务策略略低。": "Kc975cd5a",
"资源配置": "K8e7a0f80",
"角色": "Kf644225f",
"设置角色的权限范围。": "K95c3fd8b",
"系统级别角色": "K138facd3",
"添加角色": "K6eac768d",
@@ -546,7 +588,7 @@
"移除成员": "K395acc14",
"添加成员": "Kec46a57f",
"输入姓名查找": "K48724410",
"搜索用户名、邮箱": "Kb9052305",
"输入名称查找用户": "Kf5fd27ed",
"设置团队和成员,然后你可以在团队内创建服务和消费者、订阅API,成员只能看到所属团队内的服务和消费者。": "K4c72fb6f",
"添加团队": "K510cdd27",
"输入名称、ID、负责人查找团队": "K9244ae14",
@@ -568,6 +610,7 @@
"退出全屏": "Kaf70c3b",
"(0)调用详情": "Kd22841a4",
"消费者调用统计": "K61cca533",
"消费者": "K7acfcfad",
"请选择消费者": "Kdfff59d4",
"调用趋势": "K8c7f2d2e",
"(0)-(1)调用趋势": "K657c3452",
@@ -600,6 +643,7 @@
"暂无调用量统计数据": "Kcd125e4d",
"暂无报文量统计数据": "Kaa114e8b",
"报文量统计": "K3ad84406",
"运行视图": "K714c192d",
"集群配置并开启监控": "Kfa088d49",
"监控功能用于辅助管理集群内信息,请配置集群、设置监控信息后查看当前集群监控情况;": "K3da3b9a0",
"集群配置": "Kaddacfb",
@@ -616,6 +660,19 @@
"暂无API数据": "K6b75bdbc",
"搜索或选择消费者": "Kb684c806",
"申请理由": "K4b15d6f5",
"支持把当前服务对接主流的 AI Agent平台,实现在 Agent 平台上快速、安全和合规地使用企业开放的 API 能力。": "K2ec0fa56",
"可按以下步骤进行对接:": "K35f23b64",
"步骤一:Agent 平台上创建自定义插件": "Kf5cd608b",
"不同 Agent 平台的操作细节可查看": "K4c81c7b6",
"《 Agent 对接手册》": "K275f7ffa",
"步骤二:导入 API 文档数据": "K49b81d06",
"可通过以下 URL 或 下载 Json 文件,导入 API 文档数据到 Agent 平台中。": "K4a3b62be",
"复制 URL": "K42697e11",
"下载 Json 文件": "K27a809c5",
"步骤三:配置 API 密钥": "K1e61fdee",
"在": "K55912595",
"菜单中,选择已通过本 API 服务申请的消费者,": "K33b1bc3",
"把 \"访问权限\" 菜单下的密钥填入到 Agent 平台对应的插件密钥配置中。": "K62adc41e",
"消费者管理": "Ke0fbd1c8",
"鉴权类型": "Kb71b5a13",
"Iss": "K4d1465ee",
@@ -676,6 +733,7 @@
"配置 Open Api": "K7829bb78",
"Open Api": "Kcdf76005",
"调用服务": "Ke2601944",
"系统拓扑图": "Kd57dfe97",
"放大": "K8504bca8",
"缩小": "K693c1b41"
}
@@ -319,7 +319,7 @@
"Ka46b9b24": "Data Source Type",
"Kbb0cdcd0": "Data Source Address",
"Kd9dfb884": "Organization",
"K3e770a75": "Authentication Token",
"K3e770a75": "Credentials Token",
"K8ef69ee2": "Key",
"Kba3507d6": "Upload Key",
"K93ac0f23": "Key file suffix is usually .key",
@@ -365,7 +365,7 @@
"Kcef64f4d": "Default AI Provider",
"Kcab588a9": "No AI model provider set,",
"Kb9b56111": "Set Now",
"Kcf756b7a": "API Call Prefix",
"Kcf756b7a": "API Request Prefix",
"K13edc043": "As a prefix for all APIs in the service, e.g., host/{service_name}/{api_path}, cannot be modified once saved.",
"Kf52a584d": "Service Category",
"K72b21be5": "Set the category in which the service will be displayed in the service marketplace",
@@ -391,8 +391,8 @@
"K813e1c0a": "Team Name",
"K692f5aa6": "Team ID",
"K5de0bc2": "Team ID can be used to retrieve the team, cannot be modified once saved.",
"Ka63dd985": "Team Leader",
"Ka6bcd272": "The leader has management authority over the team, services, and members within the team",
"Ka63dd985": "Team Administrator",
"Ka6bcd272": "The Administrator has management authority over the team, services, and members within the team",
"Ka2012bdd": "Delete Team",
"Kbde1f3d": "Service data must be cleared before deletion",
"K395acc14": "Remove",
@@ -463,7 +463,7 @@
"Kd59290a2": "Search Category or Tag",
"K6b75bdbc": "No API Data",
"K4b15d6f5": "Application Reason",
"Kb71b5a13": "Authentication Type",
"Kb71b5a13": "Credentials Type",
"K4d1465ee": "ISS",
"K5dcd7ed8": "Signature Algorithm",
"K5b0eedd3": "Secret",
@@ -475,10 +475,10 @@
"K31418470": "SK",
"Kbfeb5297": "API Key",
"K1a78e6f0": "Expiration Time",
"Ke64e43a": "Hide Authentication Info",
"K217cb125": "Authentication Details",
"K2bb63eca": "Add Authentication",
"Kd74d69b7": "Edit Authentication",
"Ke64e43a": "Hide Credentials Info",
"K217cb125": "Credentials Details",
"K2bb63eca": "Add Credentials",
"Kd74d69b7": "Edit Credentials",
"K9cbe1e0": "Modify",
"Kb6e9328f": "Authorization",
"Kd23d1716": "Add Authorization",
@@ -678,5 +678,90 @@
"K1644b775": "Add",
"Kc8ee3e62": "Non-Existence",
"K1e97dbd8": "Existence",
"Kec91f0db": "Applicant Consumer"
"Kec91f0db": "Applicant Consumer",
"Kf5fd27ed": "Enter Name to Search User",
"K2ec0fa56": "Support integration with mainstream AI Agent platforms to enable the use of enterprise APIs in a fast, secure, and compliant manner on the Agent platform.",
"K35f23b64": "Follow these steps for integration:",
"Kf5cd608b": "Step 1: Create a custom plugin on the Agent platform",
"K4c81c7b6": "For details on the operations of different Agent platforms, refer to",
"K275f7ffa": "the Agent Integration Manual",
"K49b81d06": "Step 2: Import API documentation data",
"K4a3b62be": "You can import API documentation data to the Agent platform via the following URL or by downloading the JSON file.",
"K42697e11": "Copy URL",
"K27a809c5": "Download JSON file",
"K1e61fdee": "Step 3: Configure API keys",
"K55912595": "In the",
"K33b1bc3": "menu, select the consumer that has applied for the API service,",
"K62adc41e": "and fill in the key from the 'Access Permission' menu into the corresponding plugin key configuration on the Agent platform.",
"Ke8cbb878": "Global Policy",
"K34d0d409": "Add Policy",
"Kbb4298ac": "Enter name, filter criteria to search",
"Kabac9caf": "Data Masking",
"Kc975cd5a": "Supports unified policy configuration for the system globally, simplifying management and ensuring consistency. The priority of global policies is slightly lower than that of service policies.",
"K52f72551": "Service Policy",
"K931615d7": "Policy Name",
"K31faa2a1": "Priority",
"Kbdec9fa": "Filter Criteria",
"Kbcbb7391": "Processed Count",
"Kad207008": "Edit",
"K630c9e6d": "APIPark",
"Ka3e9f580": "Release Name",
"Kb2480682": "Policy List",
"K118d8d74": "Data Format",
"Kfe7c7d2d": "Keyword",
"K2f57a694": "Regular Expression",
"K8953e0a6": "Mobile Number",
"K6f86a038": "ID Card Number",
"K7954e7c8": "Bank Card Number",
"K320fdb17": "Amount",
"K7867acda": "Date",
"K7d327ae8": "Partial Display",
"Kfbf38e3c": "Partial Masking",
"Kd8c1fbb0": "Truncate",
"K89829921": "Replace",
"K480a7165": "Shuffle",
"Kea0d69df": "Random String",
"Ke7c84d1d": "Custom String",
"K49731763": "Please enter IP address or CIDR range, each separated by a newline.",
"K83237c89": "The entered IP or CIDR does not meet the format.",
"K5ae2c87a": "Please enter the correct path, such as /usr/* or */usr/*.",
"Kc82b8374": "Edit Policy",
"K4b34a5e5": "Policy Type",
"K57f0fee8": "Match Conditions",
"K10650c58": "Data Masking Rules",
"K1b34a9ab": "Configure Masking Rules",
"K26d22405": "Match Value",
"K1546e1fe": "Masking Type",
"K9b9b0629": "Starting Position",
"K52c84fe1": "Length",
"Kde84409c": "Replace Type",
"K338653b4": "Replace Value",
"Kbaeed3b7": "JSON Path",
"K4cd91d61": "Masking Rules",
"K8dcad979": "Custom String; Value:",
"K82e3f7b7": "Starting Position: (0); Length: (1)",
"K49dfc123": "Selected (0) items (1) data",
"K8457ea34": "All (0)",
"K7ca9a795": "Attribute Name",
"Kc4391744": "Attribute Value",
"K678e13fc": "Configure (0)",
"K508d8bf4": "Integration Address",
"K67f4e9bb": "The domain name for obtaining API market documentation information when integrating with external platforms",
"K1da86266": "Invalid",
"K3a34d49b": "Pending Update",
"Kd2850420": "Pending Deletion",
"K9ada4366": "Operation successful, the page will refresh shortly",
"K9b332ab1": "Request prefix",
"K3d78d483": "HTTP headers",
"K17dc3a62": "Data logs",
"Ke429194e": "Processing logs",
"K84ffb1dd": "Enter the invocation address, consumer IP, and consumer condition to search",
"Kb147fabc": "Create",
"K40ca4f2": "Update",
"K3e7aa0ad": "Content",
"K2f5fdf5e": "Call Address",
"K1bc5e0a3": "Consumer IP",
"K6f39ea21": "Authentication Name",
"K8c34c02f": "Before Masking",
"K8e3d388d": "After Masking"
}
@@ -700,5 +700,90 @@
"K1644b775": "追加",
"Kc8ee3e62": "存在しない",
"K1e97dbd8": "存在する",
"Kec91f0db": "申請側コンシューマー"
"Kec91f0db": "申請側コンシューマー",
"Kf5fd27ed": "名前を入力してユーザーを検索",
"K2ec0fa56": "主要なAIエージェントプラットフォームと連携し、エージェントプラットフォーム上で企業のAPIを迅速、安全、かつコンプライアンスに準拠して使用できるようサポートします。",
"K35f23b64": "以下の手順で統合を行います:",
"Kf5cd608b": "ステップ1:エージェントプラットフォームでカスタムプラグインを作成",
"K4c81c7b6": "異なるエージェントプラットフォームの操作詳細については、",
"K275f7ffa": "「エージェント統合マニュアル」を参照してください。",
"K49b81d06": "ステップ2:APIドキュメントデータのインポート",
"K4a3b62be": "以下のURLを使用するか、JSONファイルをダウンロードして、APIドキュメントデータをエージェントプラットフォームにインポートできます。",
"K42697e11": "URLをコピー",
"K27a809c5": "JSONファイルをダウンロード",
"K1e61fdee": "ステップ3APIキーの設定",
"K55912595": "「",
"K33b1bc3": "メニュー」から、APIサービスを申し込んだ消費者を選択し、",
"K62adc41e": "「アクセス権限」メニューのキーをエージェントプラットフォームのプラグインキー設定に入力します。",
"Ke8cbb878": "グローバルポリシー",
"K34d0d409": "ポリシーを追加",
"Kbb4298ac": "名前、フィルタ条件を入力して検索",
"Kabac9caf": "データマスキング",
"Kc975cd5a": "システム全体で統一されたポリシー設定をサポートし、管理の簡素化と一貫性の確保を実現します。グローバルポリシーの優先度はサービスポリシーより少し低いです。",
"K52f72551": "サービスポリシー",
"K931615d7": "ポリシー名",
"K31faa2a1": "優先度",
"Kbdec9fa": "フィルタ条件",
"Kbcbb7391": "処理数",
"Kad207008": "編集",
"K630c9e6d": "APIPark",
"Ka3e9f580": "リリース名",
"Kb2480682": "ポリシーリスト",
"K118d8d74": "データフォーマット",
"Kfe7c7d2d": "キーワード",
"K2f57a694": "正規表現",
"K8953e0a6": "携帯番号",
"K6f86a038": "IDカード番号",
"K7954e7c8": "銀行カード番号",
"K320fdb17": "金額",
"K7867acda": "日付",
"K7d327ae8": "部分表示",
"Kfbf38e3c": "部分マスキング",
"Kd8c1fbb0": "切り取り",
"K89829921": "置換",
"K480a7165": "シャッフル",
"Kea0d69df": "ランダム文字列",
"Ke7c84d1d": "カスタム文字列",
"K49731763": "IPアドレスまたはCIDR範囲を入力してください。各行で改行してください。",
"K83237c89": "入力されたIPまたはCIDRがフォーマットに一致しません。",
"K5ae2c87a": "正しいパスを入力してください。例: /usr/* または */usr/*。",
"Kc82b8374": "ポリシーを編集",
"K4b34a5e5": "ポリシータイプ",
"K57f0fee8": "一致条件",
"K10650c58": "データマスキングルール",
"K1b34a9ab": "マスキングルールを設定",
"K26d22405": "一致値",
"K1546e1fe": "マスキングタイプ",
"K9b9b0629": "開始位置",
"K52c84fe1": "長さ",
"Kde84409c": "置換タイプ",
"K338653b4": "置換値",
"Kbaeed3b7": "JSON パス",
"K4cd91d61": "マスキングルール",
"K8dcad979": "カスタム文字列; 値:",
"K82e3f7b7": "開始位置: (0); 長さ: (1)",
"K49dfc123": "選択された (0) アイテム (1) データ",
"K8457ea34": "すべて (0)",
"K7ca9a795": "属性名",
"Kc4391744": "属性値",
"K678e13fc": "設定 (0)",
"K508d8bf4": "統合アドレス",
"K67f4e9bb": "外部プラットフォームと統合する際に、API市場のドキュメント情報を取得するためのドメイン名",
"K1da86266": "無効",
"K3a34d49b": "更新待ち",
"Kd2850420": "削除待ち",
"K9ada4366": "操作成功、ページを更新します",
"K9b332ab1": "リクエストプレフィックス",
"K3d78d483": "HTTPヘッダー",
"K17dc3a62": "データログ",
"Ke429194e": "ログの処理",
"K84ffb1dd": "呼び出しアドレス、コンシューマーIP、条件を入力して検索",
"Kb147fabc": "新規作成",
"K40ca4f2": "更新",
"K3e7aa0ad": "内容",
"K2f5fdf5e": "呼び出しアドレス",
"K1bc5e0a3": "コンシューマー IP",
"K6f39ea21": "認証名",
"K8c34c02f": "マスキング前",
"K8e3d388d": "マスキング後"
}
@@ -1,4 +1,8 @@
{
"K630c9e6d": "APIPark",
"Ka3e9f580": "发布名称",
"Kb2480682": "策略列表",
"K1da86266": "无效",
"K76036e25": "HTTP 请求头",
"K44607e3f": "全等匹配",
"Kc287500a": "前缀匹配",
@@ -21,6 +25,47 @@
"K78b1ca25": "地址",
"K1644b775": "新增",
"Kec91f0db": "申请方消费者",
"Kc3b7bfa8": "暂无消费者描述",
"K3a6f905d": "输入名称、ID 查找消费者"
"K118d8d74": "数据格式",
"Kfe7c7d2d": "关键字",
"K2f57a694": "正则表达式",
"K8953e0a6": "手机号",
"K6f86a038": "身份证号",
"K7954e7c8": "银行卡号",
"K320fdb17": "金额",
"K7867acda": "日期",
"K7d327ae8": "局部显示",
"Kfbf38e3c": "局部遮蔽",
"Kd8c1fbb0": "截取",
"K89829921": "替换",
"K480a7165": "乱序",
"Kea0d69df": "随机字符串",
"Ke7c84d1d": "自定义字符串",
"K49731763": "请输入IP地址或CIDR范围,每条以换行分割",
"K3a34d49b": "待更新",
"Kd2850420": "待删除",
"K83237c89": "输入的IP或CIDR不符合格式",
"K5ae2c87a": "请正确输入路径,如/usr/*或*/usr/*",
"K508d8bf4": "集成地址",
"K67f4e9bb": "与外部平台集成时,获取 API 市场中文档信息的域名",
"Kc82b8374": "编辑策略",
"K4b34a5e5": "策略类型",
"K57f0fee8": "匹配条件",
"K10650c58": "数据脱敏规则",
"K1b34a9ab": "配置脱敏规则",
"K26d22405": "匹配值",
"K1546e1fe": "脱敏类型",
"K9b9b0629": "起始位置",
"K52c84fe1": "长度",
"Kde84409c": "替换类型",
"K338653b4": "替换值",
"Kbaeed3b7": "JSON Path",
"K4cd91d61": "脱敏规则",
"K8dcad979": "自定义字符串; 值:",
"K82e3f7b7": "起始位置:(0)位;长度:(1)位",
"K49dfc123": "已选择(0)项(1)数据",
"K8457ea34": "所有(0)",
"K7ca9a795": "属性名称",
"Kc4391744": "属性值",
"K678e13fc": "配置(0)",
"Kf5fd27ed": "输入名称查找用户"
}
@@ -1,3 +1,16 @@
{
"Kc0e5ef9f": "Workspace",
"K4de11e23": "Home",
"K61c89f5f": "API Portal",
"K3fe97dcc": "System Settings",
"Kecbb0e45": "System",
"Ka358e23d": "General",
"K449058e9": "API Gateway",
"K99935e6f": "AI Model",
"K1deaa2dd": "User",
"K631d646f": "Open API",
"K1196b104": "APIPark",
"K5cfdd950": "This data cannot be recovered after deletion. Are you sure you want to delete?",
"Kb9052305": "Search Username, Email",
"K40a89bd8": "Enter Name, ID to Search Member"
}
@@ -1,10 +1,23 @@
{
"Kc0e5ef9f": "Workspace",
"K4de11e23": "Home",
"Kfe93ef35": "Application",
"K61c89f5f": "API Portal",
"K3fe97dcc": "設定",
"Kecbb0e45": "システム",
"Ka358e23d": "一般",
"K449058e9": "API ゲートウェイ",
"K99935e6f": "AI モデル",
"K1deaa2dd": "ユーザー",
"K631d646f": "Open API",
"K1196b104": "APIPark",
"Kffd7e274": "無審査:すべてのアプリケーションがこのサービスにサブスクライブできます",
"K8a8b13e4": "手動審査:承認されたアプリケーションのみがこのサービスにサブスクライブできます",
"K9bdd8403": "API を安全に呼び出すためには、アプリケーションとトークンを作成する必要があります。",
"Kc8239422": "チームにはユーザー、アプリケーション、サービスが含まれ、異なるチームのアプリケーションとサービスのデータは分離されています。企業内の部門/プロジェクトグループ/チームの管理に使用できます。",
"Ka0a8840a": "他のアプリケーションのサブスクリプション申請をレビューし、承認後に API リクエストが発行できます。",
"K5cfdd950": "このデータを削除すると、復元できません。削除しますか?",
"Kb9052305": "ユーザー名またはメールを検索",
"K5ece3bac": "チームとメンバーを設定してから、チーム内でサービスとアプリケーションを作成し、API をサブスクライブできます。メンバーは所属チーム内のサービスとアプリケーションのみを表示できます。",
"K1512e983": "アプリケーション呼び出し統計",
"Kb4d2007f": "Application を選択",
@@ -1,10 +1,23 @@
{
"Kc0e5ef9f": "工作空间",
"K4de11e23": "首页",
"Kfe93ef35": "消费者",
"K61c89f5f": "API 门户",
"K3fe97dcc": "系统设置",
"Kecbb0e45": "系统",
"Ka358e23d": "常规",
"K449058e9": "API 网关",
"K99935e6f": "AI 模型",
"K1deaa2dd": "用户",
"K631d646f": "Open API",
"K1196b104": "APIPark",
"Kffd7e274": "无审核:允许所有消费者订阅该服务",
"K8a8b13e4": "人工审核:仅允许审核通过的消费者订阅该服务",
"K9bdd8403": "为了安全地调用 API,你需要创建一个消费者以及Token。",
"Kc8239422": "团队中包含了人员、消费者和服务,不同团队之间的消费者和服务数据是隔离的,可用于管理企业内部不同的部门/项目组/团队。",
"Ka0a8840a": "审核其他消费者的订阅申请,审核通过后的才可发起 API 请求。",
"K5cfdd950": "该数据删除后将无法找回,是否删除?",
"Kb9052305": "搜索用户名、邮箱",
"K5ece3bac": "设置团队和成员,然后你可以在团队内创建服务和消费者、订阅API,成员只能看到所属团队内的服务和消费者。",
"K1512e983": "消费者调用统计",
"Kb4d2007f": "请选择消费者",
@@ -1,10 +1,23 @@
{
"Kc0e5ef9f": "工作區",
"K4de11e23": "主頁",
"Kfe93ef35": "應用程式",
"K61c89f5f": "API 門戶",
"K3fe97dcc": "系統設置",
"Kecbb0e45": "系統",
"Ka358e23d": "常規",
"K449058e9": "API 網關",
"K99935e6f": "AI 模型",
"K1deaa2dd": "用戶",
"K631d646f": "Open API",
"K1196b104": "APIPark",
"Kffd7e274": "無審核:允許所有應用程式訂閱該服務",
"K8a8b13e4": "人工審核:僅允許審核通過的應用程式訂閱該服務",
"K9bdd8403": "為了安全地調用 API,你需要創建一個應用以及Token。",
"Kc8239422": "團隊中包含了人員、應用程式和服務,不同團隊之間的應用程式和服務數據是隔離的,可用於管理企業內部不同的部門/項目組/團隊。",
"Ka0a8840a": "審核其他應用程式的訂閱申請,審核通過後的才可發起 API 請求。",
"K5cfdd950": "該數據刪除後將無法找回,是否刪除?",
"Kb9052305": "搜索用戶名、電郵",
"K5ece3bac": "設置團隊和成員,然後你可以在團隊內創建服務和應用程式、訂閱API,成員只能看到所屬團隊內的服務和應用程式。",
"K1512e983": "應用程式調用統計",
"Kb4d2007f": "請選擇應用程式",
File diff suppressed because it is too large Load Diff
@@ -700,5 +700,90 @@
"K1644b775": "新增",
"Kc8ee3e62": "不存在匹配",
"K1e97dbd8": "存在匹配",
"Kec91f0db": "申請方消費者"
"Kec91f0db": "申請方消費者",
"Kf5fd27ed": "輸入名稱查找使用者",
"K2ec0fa56": "支援將當前服務對接主流的 AI Agent 平台,實現在 Agent 平台上快速、安全和合規地使用企業開放的 API 能力。",
"K35f23b64": "可按以下步驟進行對接:",
"Kf5cd608b": "步驟一:Agent 平台上創建自定義插件",
"K4c81c7b6": "不同 Agent 平台的操作細節可查看",
"K275f7ffa": "《 Agent 對接手冊》",
"K49b81d06": "步驟二:導入 API 文檔數據",
"K4a3b62be": "可通過以下 URL 或下載 json 文件,導入 API 文檔數據到 Agent 平台中。",
"K42697e11": "複製 URL",
"K27a809c5": "下載 Json 文件",
"K1e61fdee": "步驟三:配置 API 密鑰",
"K55912595": "在",
"K33b1bc3": "菜單中,選擇已通過本 API 服務申請的消費者,",
"K62adc41e": "把「訪問權限」菜單下的密鑰填入到 Agent 平台對應的插件密鑰配置中。",
"Ke8cbb878": "全域策略",
"K34d0d409": "新增策略",
"Kbb4298ac": "輸入名稱、篩選條件查找",
"Kabac9caf": "資料脫敏",
"Kc975cd5a": "支援對系統全域進行統一的策略配置,從而簡化管理並確保一致性。全域策略的優先級比服務策略略低。",
"K52f72551": "服務策略",
"K931615d7": "策略名稱",
"K31faa2a1": "優先級",
"Kbdec9fa": "篩選條件",
"Kbcbb7391": "處理數",
"Kad207008": "編輯",
"K630c9e6d": "APIPark",
"Ka3e9f580": "發布名稱",
"Kb2480682": "策略列表",
"K118d8d74": "數據格式",
"Kfe7c7d2d": "關鍵字",
"K2f57a694": "正則表達式",
"K8953e0a6": "手機號碼",
"K6f86a038": "身份證號碼",
"K7954e7c8": "銀行卡號碼",
"K320fdb17": "金額",
"K7867acda": "日期",
"K7d327ae8": "局部顯示",
"Kfbf38e3c": "局部遮蔽",
"Kd8c1fbb0": "截取",
"K89829921": "替換",
"K480a7165": "亂序",
"Kea0d69df": "隨機字串",
"Ke7c84d1d": "自訂字串",
"K49731763": "請輸入IP地址或CIDR範圍,每行以換行符分隔",
"K83237c89": "輸入的IP或CIDR格式不正確",
"K5ae2c87a": "請正確輸入路徑,例如/usr/*或*/usr/*",
"Kc82b8374": "編輯策略",
"K4b34a5e5": "策略類型",
"K57f0fee8": "匹配條件",
"K10650c58": "數據脫敏規則",
"K1b34a9ab": "配置脫敏規則",
"K26d22405": "匹配值",
"K1546e1fe": "脫敏類型",
"K9b9b0629": "起始位置",
"K52c84fe1": "長度",
"Kde84409c": "替換類型",
"K338653b4": "替換值",
"Kbaeed3b7": "JSON路徑",
"K4cd91d61": "脫敏規則",
"K8dcad979": "自訂字串; 值:",
"K82e3f7b7": "起始位置:(0)位;長度:(1)位",
"K49dfc123": "已選擇(0)項(1)數據",
"K8457ea34": "所有(0)",
"K7ca9a795": "屬性名稱",
"Kc4391744": "屬性值",
"K678e13fc": "配置(0)",
"K508d8bf4": "集成地址",
"K67f4e9bb": "與外部平台集成時,用於獲取 API 市場文檔信息的域名",
"K1da86266": "無效",
"K3a34d49b": "待更新",
"Kd2850420": "待刪除",
"K9ada4366": "操作成功,即將刷新頁面",
"K9b332ab1": "請求前綴",
"K3d78d483": "HTTP 標頭",
"K17dc3a62": "數據日誌",
"Ke429194e": "處理日誌",
"K84ffb1dd": "輸入調用地址、消費者 IP 和消費者條件進行查找",
"Kb147fabc": "新建",
"K40ca4f2": "更新",
"K3e7aa0ad": "內容",
"K2f5fdf5e": "調用地址",
"K1bc5e0a3": "消費者 IP",
"K6f39ea21": "鑑權名稱",
"K8c34c02f": "脫敏前",
"K8e3d388d": "脫敏後"
}
@@ -1,25 +1,34 @@
import { Icon } from "@iconify/react/dist/iconify.js";
import { MenuProps } from "antd";
export type MenuItem = Required<MenuProps>['items'][number];
export function getNavItem(
label: React.ReactNode,
key: React.Key,
path:string,
icon?: React.ReactNode,
children?: MenuItem[],
type?: 'group',
access?:string[] | string
): MenuItem {
export function getNavItem({
label,
key,
path,
icon,
children,
type,
access
}: {
label: React.ReactNode;
key: React.Key;
path?: string;
icon?: React.ReactNode;
children?: MenuItem[];
type?: 'group';
access?: string[] | string;
}): MenuItem {
return {
key,
icon :icon ,
path,
icon,
routes:children,
name:label,
type,
access
access,
path
} as MenuItem;
}
@@ -53,4 +62,19 @@ export function getItem(
label,
access
}
}
}
export function transformMenuData(data: any[]): MenuItem[] {
return data.map(item => {
const { name, key, path, icon, children, access } = item;
return getNavItem({
label:name,
key,
path,
icon: icon ? <Icon icon={icon} width="18" height="18" /> : undefined,
children:children ? transformMenuData(children) : undefined,
access}
);
});
}
@@ -13,10 +13,15 @@ export const checkAccess:(access:AccessDataType, accessData:Map<string,string[]>
return false
}
const neededBackendAccessArr = PERMISSION_DEFINITION[0]?.[access]?.granted?.anyOf[0].backend || []
return accessData?.has(accLevel)&& accessData.get(accLevel)!.length > 0 ? hasIntersection(neededBackendAccessArr, accessData.get(accLevel)!) : false
let accessSet = new Set(accessData.get('system'))
if(accLevel === 'team'){
accessSet = new Set(Array.from(accessSet).concat(accessData?.get('team') || []))
}
return accessSet!.size > 0 ? hasIntersection(neededBackendAccessArr, accessSet) : false
}
const hasIntersection = (arr1:string[], arr2:string[])=> {
const hasIntersection = (arr1:string[], set1:Set<string>)=> {
const arr2 = Array.from(set1)
const set = new Set(arr1.length > arr2.length ? arr2:arr1)
const arr = arr1.length > arr2.length ? arr1:arr2
for (const item of arr) {
@@ -0,0 +1,154 @@
import { CoreObj, PluginConfigType, PluginRouterConfig, RouteConfig, RouterMapConfig } from '@common/const/type';
import { isFunction } from 'lodash-es'
// @ts-expect-error module cannot find
import { __federation_method_setRemote,__federation_method_getRemote,__federation_method_unwrapDefault } from 'virtual:__federation__';
import { ApiparkPluginDriverType } from '@common/const/type';
import React from 'react';
interface RemoteModuleConfig{
type:string
remoteEntry:string
exposedModule:string
remoteName:string
}
export async function loadRemoteModule(remoteModuleConfig:RemoteModuleConfig){
__federation_method_setRemote(remoteModuleConfig.remoteName, {
url: () => Promise.resolve(remoteModuleConfig.remoteEntry),
format: 'esm',
from: 'vite',
});
return await __federation_method_getRemote(
remoteModuleConfig.remoteName,
`./${remoteModuleConfig.exposedModule}`
)
}
export function generateRemoteModuleTemplate(
pluginName: string,
exposedModule: string,
pluginPath: string
):RemoteModuleConfig {
return {
type: 'module',
remoteEntry: pluginPath,
exposedModule,
remoteName: pluginName
}
}
/** check the lifecycle method of plugin */
export function validateExportLifecycle(exports: unknown) {
const { bootstrap, mount, unmount } = exports ?? {}
return isFunction(bootstrap) && isFunction(mount) && isFunction(unmount)
}
export const DEFAULT_LOCAL_PLUGIN_PATH = '/plugin-frontend/'
export const ApiparkPluginDriver = (routerMap: Map<string, RouterMapConfig>): ApiparkPluginDriverType => { return {
builtIn: {
component: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {
if(!coreObj || !pluginConfig) return coreObj
for (const pluginRouter of pluginConfig.router) {
routerMap.get(pluginConfig.name) && coreObj.pluginProvider.setRouterConfig(pluginRouter.type === 'root' , {
...routerMap.get(pluginConfig.name)!,
key:pluginConfig.name,
path:pluginRouter.path})
}
return coreObj
}
},
remote: {
normal: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {
if(!coreObj || !pluginConfig) return coreObj
const routerToChanged:RouteConfig[] = coreObj.routerConfig.find((router: RouteConfig) => router.path === '/' && router?.pathMatch !== 'full')!.children as RouteConfig[]
const remoteRouter:RouteConfig[] = routerToChanged.find((item:RouteConfig) => item?.data?.['type'] === 'remotePlugin') as RouteConfig[]
if (!remoteRouter) {
routerMap.get('remote') && coreObj.pluginProvider.setRouterConfig(false,{
...routerMap.get('remote')!, key:'remote', path:'remote',type:'remotePlugin',children:[{
path:':moduleName',
component: routerMap.get('remote')!.component}
]
})
}
return coreObj
}
},
intelligent: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
normal: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {
if(!coreObj || !pluginConfig) return coreObj
if(['logsettings','resourcesettings'].indexOf(pluginConfig.name) !== -1){
const routerToChanged:RouteConfig[] = coreObj.routerConfig.find((router: RouteConfig) => router.path === '/' && router?.pathMatch !== 'full')!.children as RouteConfig[]
const remoteRouter:RouteConfig[] = routerToChanged.find((item:RouteConfig) => item?.data?.['key'] === pluginConfig.name) as RouteConfig[]
if(!remoteRouter){
routerMap.get(pluginConfig.name) && routerToChanged.unshift({...routerMap.get(pluginConfig.name)!, key:pluginConfig.name, path:pluginConfig.path})
}
return
}
const remoteRouter = coreObj.routerConfig.find((item:RouteConfig) => item?.data?.['type'] === 'intelligentPlugin')
if (!remoteRouter) {
// coreObj.pluginProvider.setRouterConfig(false, {
// path: 'template',
// loadChildren: coreObj.builtInPluginLoader('intelligent'),
// data: {
// type: 'intelligentPlugin'
// }
// }, coreObj.routerConfig)
}
return coreObj
}
},
local: {
router: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {
if(!coreObj || !pluginConfig) return coreObj
for (const pluginRouter of pluginConfig.router) {
if (pluginRouter.type === 'sub') {
continue
}
updateRouterConfigWithPlugin(coreObj, pluginRouter, pluginConfig)
}
return coreObj
},
preload: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {
if(!coreObj || !pluginConfig) return coreObj
coreObj.setExecuteList(prev=>[...prev,{ ...pluginConfig, expose: 'Bootstrap', bootstrap: 'BootstrapModule.bootstrap' }])
for (const pluginRouter of pluginConfig.router) {
updateRouterConfigWithPlugin(coreObj, pluginRouter, pluginConfig)
}
return coreObj
}
// extender: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {}
}
}
}
async function updateRouterConfigWithPlugin (coreObj: CoreObj, pluginRouter: PluginRouterConfig, pluginConfig: PluginConfigType) {
if (!pluginRouter.expose) {
throw new Error('pluginRouter.expose is required')
} else {
for (const pluginRouter of pluginConfig.router) {
const loadedModule = await coreObj.pluginLoader.loadModule(
pluginRouter.path,
pluginConfig.name,
pluginRouter.expose!,
pluginConfig.path || `${DEFAULT_LOCAL_PLUGIN_PATH}${pluginConfig.name}/apipark.js`
)
const loadedModulePage = loadedModule[pluginRouter.expose!]
const LazyComponent = React.lazy(() => Promise.resolve({ default: loadedModulePage?.default || loadedModulePage }));
const newRouter: RouteConfig = {
path: pluginRouter.path,
key: pluginConfig.name,
lazy: () => Promise.resolve({ default: (props: any) => <LazyComponent {...props} /> }),
pathPrefix: pluginRouter.path.endsWith('/*') ? pluginRouter.path.slice(0, -2) : pluginRouter.path,
lifecycle:{
canActivate: loadedModule?.beforeMount,
canLoad: loadedModule?.mount,
canDeactivate: loadedModule?.beforeUnmount,
deactivated: loadedModule?.unmount
}
};
coreObj.pluginProvider.setRouterConfig(pluginRouter.type === 'root' , newRouter)
}
}
}
@@ -4,5 +4,38 @@ export const validateUrlSlash = (_, value) => {
if (value && value.includes('//')) {
return Promise.reject(new Error($t('暂不支持带有双斜杠//的url')));
}
return Promise.resolve();
};
export const validateIPorCIDR = (rule, value) => {
if (!value) {
return Promise.resolve();
}
const lines = value.split('\n');
const ipCidrRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$/;
for (const line of lines) {
if (line && !ipCidrRegex.test(line.trim())) {
return Promise.reject($t('输入的IP或CIDR不符合格式'));
}
}
return Promise.resolve();
};
export const validateApiPath = (rule, value) => {
if (!value) {
return Promise.resolve();
}
const invalidCharsRegex = /[^a-zA-Z0-9\-\/\*]/;
const validPathRegex = /^(\/?\*?\/?[a-zA-Z0-9\-\/\*]*)$/;
if (value && (invalidCharsRegex.test(value.trim()) || !validPathRegex.test(value.trim()))) {
return Promise.reject($t('请正确输入路径,如/usr/*或*/usr/*'));
}
return Promise.resolve();
};
+26 -6
View File
@@ -1,16 +1,36 @@
<!doctype html>
<html lang="en">
<head>
<head id="head">
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/frontend/favicon.ico" />
<link id="favicon" rel="icon" type="image/svg+xml" href="/frontend/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>APIPark</title>
</head>
<body>
<body id="eo-body">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script async src="/frontend/iconpark_eolink.js"></script>
<script async src="/frontend/iconpark_apinto.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const eoBody = document.getElementById('eo-body');
const favicon = document.getElementById('favicon');
const createScript = (id, src) => {
const script = document.createElement('script');
script.id = id;
script.async = true;
script.src = src;
return script;
};
const iconparkApintoSrc = window.location.hostname === 'localhost' ? '/iconpark_apinto.js' : '/frontend/iconpark_apinto.js';
const iconparkEolinkSrc = window.location.hostname === 'localhost' ? '/iconpark_eolink.js' : '/frontend/iconpark_eolink.js';
const faviconSrc = window.location.hostname === 'localhost' ? '/favicon.ico' : '/frontend/favicon.ico';
favicon.href = faviconSrc;
eoBody.appendChild(createScript('iconpark_apinto', iconparkApintoSrc));
eoBody.appendChild(createScript('iconpark_eolink', iconparkEolinkSrc));
});
</script>
</body>
</html>
+17
View File
@@ -290,3 +290,20 @@ a{
transition: background-color 0s 600000s, color 0s 600000s !important;
}
}
.ant-select-selection-overflow-item:first-child {
max-width: calc(100% - 60px);
margin-right: 4px;
}
a[disabled]:hover {
color: #BBB;
cursor: not-allowed;
}
.ant-input-group-addon{
height:32px !important;
.ant-btn.ant-btn-default{
height:32px !important;
}
}
+28 -40
View File
@@ -1,30 +1,16 @@
import './App.css'
import { ConfigProvider, ConfigProviderProps, Radio, RadioChangeEvent } from 'antd';
import { ConfigProvider, App as AppAntd } from 'antd';
import RenderRoutes from '@core/components/aoplatform/RenderRoutes';
import {BreadcrumbProvider} from "@common/contexts/BreadcrumbContext.tsx";
import { StyleProvider } from '@ant-design/cssinjs';
import zhCN from 'antd/locale/zh_CN';
import enUS from 'antd/locale/en_US';
import zhTW from 'antd/locale/zh_TW';
import jaJP from 'antd/locale/ja_JP';
import useInitializeMonaco from "@common/hooks/useInitializeMonaco";
import { useEffect, useMemo, useState } from 'react';
import 'dayjs/locale/zh-cn';
import 'dayjs/locale/zh-tw';
import 'dayjs/locale/ja';
import dayjs from 'dayjs';
import { useGlobalContext } from '@common/contexts/GlobalStateContext';
import { useMemo } from 'react';
import { GlobalProvider } from '@common/contexts/GlobalStateContext';
import { $t } from '@common/locales';
type Locale = ConfigProviderProps['locale'];
const languageMap: Record<string, Locale> = {
'zh-CN':zhCN,
'en-US':enUS,
'zh-TW':zhTW,
'ja-JP':jaJP
}
import { PluginEventHubProvider } from '@common/contexts/PluginEventHubContext';
import { PluginSlotHubProvider } from '@common/contexts/PluginSlotHubContext';
import { useLocaleContext } from '@common/contexts/LocaleContext';
import { StyleProvider } from '@ant-design/cssinjs';
const antdComponentThemeToken = {
@@ -150,33 +136,35 @@ const antdComponentThemeToken = {
function App() {
const [locale, setLocal] = useState<Locale>();
const { locale } = useLocaleContext();
useInitializeMonaco()
const { state} = useGlobalContext()
useEffect(() => {
dayjs.locale(state.language);
setLocal(languageMap[state.language]);
},[state.language])
const validateMessages = useMemo(()=>({
required: $t('必填项'),
email:$t('不是有效邮箱地址')}
),[state.language])
),[locale])
return (
<StyleProvider hashPriority={"high"}>
<ConfigProvider
locale={locale}
wave={{disabled:true}}
theme={antdComponentThemeToken}
form={{validateMessages }}>
<BreadcrumbProvider>
<RenderRoutes />
</BreadcrumbProvider>
</ConfigProvider>
</StyleProvider>
<StyleProvider hashPriority={"high"}>
<ConfigProvider
locale={locale}
wave={{disabled:true}}
theme={antdComponentThemeToken}
form={{validateMessages }}>
<PluginEventHubProvider>
<AppAntd className="h-full" message={{ maxCount: 1 }}>
<PluginSlotHubProvider>
<GlobalProvider>
<BreadcrumbProvider>
<RenderRoutes />
</BreadcrumbProvider>
</GlobalProvider>
</PluginSlotHubProvider>
</AppAntd>
</PluginEventHubProvider>
</ConfigProvider>
</StyleProvider>
);
}
@@ -1,572 +1,95 @@
import { BrowserRouter as Router, Routes, Route, Navigate, Outlet } from 'react-router-dom';
import Login from "@core/pages/Login.tsx"
import { Navigate, RouterProvider, createBrowserRouter, RouteObject } from 'react-router-dom';
import BasicLayout from '@common/components/aoplatform/BasicLayout';
import {createElement, ReactElement,ReactNode,Suspense} from 'react';
import { v4 as uuidv4 } from 'uuid'
import {App, Skeleton} from "antd";
import {createElement,Suspense, useEffect, useState} from 'react';
import {Skeleton, Spin} from "antd";
import {useGlobalContext} from "@common/contexts/GlobalStateContext.tsx";
import {FC,lazy} from 'react';
import { TeamProvider } from '@core/contexts/TeamContext.tsx';
import { TenantManagementProvider } from '@market/contexts/TenantManagementContext.tsx';
import Guide from '@core/pages/guide/Guide';
import { AiServiceProvider } from '@core/contexts/AiServiceContext';
import AiServiceOutlet from '@core/pages/aiService/AiServiceOutlet';
import SystemOutlet from '@core/pages/system/SystemOutlet';
import { SystemProvider } from '@core/contexts/SystemContext';
type RouteConfig = {
path:string
component?:ReactElement
children?:(RouteConfig|false)[]
key:string
provider?:FC<{ children: ReactNode; }>
lazy?:unknown
}
const APP_MODE = import.meta.env.VITE_APP_MODE;
export type RouterParams = {
teamId:string
apiId:string
serviceId:string
clusterId:string;
memberGroupId:string
userGroupId:string
pluginName:string
moduleId:string
accessType:'project'|'team'|'service'
categoryId:string
tagId:string
dashboardType:string
dashboardDetailId:string
topologyId:string
appId:string
roleType:string
roleId:string
routeId:string
}
const PUBLIC_ROUTES:RouteConfig[] = [
{
path:'/',
component:<Login/>,
key: uuidv4(),
},
{
path:'/login',
component:<Login/>,
key: uuidv4()
},
{
path:'/',
component:<ProtectedRoute/>,
key: uuidv4(),
children:[
// {
// path:'approval/*',
// component:<ApprovalPage />,
// key:uuidv4()
// },
{
path:'guide/*',
component:<Guide />,
key:uuidv4()
},
{
path:'team',
component:<Outlet/>,
key: uuidv4(),
provider: TeamProvider,
children:[
{
path:'',
key: uuidv4(),
component: <Navigate to="list" />
},
{
path:'list',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamList.tsx'))
},
{
path:'inside/:teamId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamInsidePage.tsx')),
key: uuidv4(),
children:[
{
path:'member',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamInsideMember.tsx')),
},
{
path:'setting',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamConfig.tsx')),
},
]
}
]
},
{
path:'service',
key: uuidv4(),
component:<SystemOutlet />,
provider: SystemProvider,
children:[
{
path:'',
key:uuidv4(),
component:<Navigate to="list" />
},
{
path:'list',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemList.tsx')),
},
{
path:'list/:teamId',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemList.tsx')),
},
{
path:':teamId',
component:<Outlet/>,
key: uuidv4(),
children:[
{
path:'inside/:serviceId',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsidePage.tsx')),
children:[
{
path:'api',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideApiDocument.tsx')),
},
{
path:'route/create',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterCreate')),
},
{
path:'route/:routeId',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterCreate')),
},
{
path:'route',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterList')),
},
{
path:'upstream',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/upstream/SystemInsideUpstreamContent.tsx')),
},
{
path:'document',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsideDocument.tsx')),
},
{
path:'subscriber',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsideSubscriber.tsx')),
children:[
]
},
{
path:'approval',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApproval.tsx')),
children:[
{
path:'',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApprovalList.tsx')),
},
{
path:'*',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApprovalList.tsx')),
}
]
},
{
path:'topology',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemTopology.tsx')),
key: uuidv4(),
children:[
]
},
{
path:'publish',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublish.tsx')),
children:[
{
path:'',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublishList.tsx')),
},
{
path:'*',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublishList.tsx')),
}
]
},
{
path:'setting',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemConfig.tsx')),
children:[
]
},
]
},
{
path:'aiInside/:serviceId',
component:<AiServiceOutlet />,
provider: AiServiceProvider,
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsidePage.tsx')),
children:[
{
path:'api',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideApiDocument')),
},
{
path:'route/create',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterCreate')),
},
{
path:'route/:routeId',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterCreate')),
},
{
path:'route',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterList')),
},
{
path:'document',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsideDocument.tsx')),
},
{
path:'subscriber',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsideSubscriber.tsx')),
children:[
]
},
{
path:'approval',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApproval')),
children:[
{
path:'',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApprovalList')),
},
{
path:'*',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApprovalList')),
}
]
},
{
path:'publish',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublish')),
children:[
{
path:'',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublishList')),
},
{
path:'*',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublishList')),
}
]
},
{
path:'setting',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemConfig.tsx')),
children:[
]
},
]
}
]
}
]
},
{
path:'datasourcing',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideDashboardSetting.tsx')),
},
{
path:'cluster',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideCluster.tsx')),
},
{
path:'aisetting',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiSetting/AiSettingList.tsx')),
},
{
path:'cert',
key: uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideCert.tsx')),
},
{
path:'serviceHub',
component:<Outlet />,
key:uuidv4(),
children:[
{
path:'',
key: uuidv4(),
component: <Navigate to="list" />
},
{
path:'list',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/ServiceHubList.tsx')),
},
{
path:'detail/:serviceId',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/ServiceHubDetail.tsx')),
}]
},
{
path:'commonsetting',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/common/CommonPage.tsx')),
key:uuidv4(),
},
{
path:'consumer',
component:<Outlet />,
provider:TenantManagementProvider,
key:uuidv4(),
children:[
{
path:'',
key:uuidv4(),
component:<Navigate to="list" />
},
{
path:':teamId/inside/:appId',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsidePage.tsx')),
children:[
{
path:'service',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsideService.tsx')),
},
{
path:'authorization',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsideAuth.tsx')),
},
{
path:'setting',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementAppSetting.tsx')),
},
]
},
{
path:'list',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ServiceHubManagement.tsx')),
},
{
path:'list/:teamId',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ServiceHubManagement.tsx')),
},
]
},
{
path:'member',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberPage.tsx')),
children:[
{
path:'',
key:uuidv4(),
component:<Navigate to="list" />
},
{
path:'list',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberList.tsx')),
},
{
path:'list/:memberGroupId',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberList.tsx')),
}
]
},
{
path:'role',
key:uuidv4(),
component:<Outlet />,
children:[
{
path: '',
key: uuidv4(),
component: <Navigate to="list" />
},
{
path:'list',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleList.tsx')),
},{
path:':roleType/config/:roleId',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleConfig.tsx')),
},{
path:':roleType/config',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleConfig.tsx')),
}
]
},
{
path:'assets',
component:<p></p>,
key:uuidv4()
},
{
path:'analytics',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/Dashboard.tsx')),
key:uuidv4(),
children:[
{
path:'total',
key:uuidv4(),
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardTotal.tsx')),
},
]
},
{
path:'template/:moduleId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
key:uuidv4()
},
{
path:'logsettings/*',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/logsettings/LogSettings.tsx')),
key: uuidv4(),
children:[{
path:'template/:moduleId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
key:uuidv4()
}]
},
APP_MODE ==='pro' && {
path:'resourcesettings/*',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/resourcesettings/ResourceSettings.tsx')),
key: uuidv4(),
children:[{
path:'template/:moduleId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
key:uuidv4()
}]
},
{
path:'userProfile/*',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/UserProfile.tsx')),
key:uuidv4(),
children:[{
path:'changepsw',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/ChangePsw.tsx')),
key:uuidv4()
}]
}
]
},
]
import usePluginLoader from '@common/hooks/pluginLoader.ts';
import { RouteConfig } from '@common/const/type.ts';
import { ApiparkPluginDriver } from '@common/utils/plugin.tsx';
import { routerMap } from '@core/const/const';
import withRouteGuard from "@common/components/aoplatform/WithRouteGuard.tsx";
import ErrorBoundary from "@common/components/aoplatform/ErrorBoundary";
import React from 'react';
import { LoadingOutlined } from '@ant-design/icons';
const RenderRoutes = ()=> {
const { loadPlugins,loadExecutedPlugin } = usePluginLoader(ApiparkPluginDriver(routerMap), routerMap)
const { routeConfig,dispatch,state } = useGlobalContext();
const [router, setRouter] = useState<unknown>(null);
useEffect(()=>{
loadPlugins().then(()=>{
loadExecutedPlugin()
})
},[])
useEffect(() => {
if (routeConfig && routeConfig.length > 0) {
const routerInstance = createBrowserRouter(generateRoutes(routeConfig));
setRouter(routerInstance);
dispatch({ type: 'SET_PLUGINS_LOADED', pluginsLoaded: true });
}
}, [routeConfig]);
if (!router || !state?.pluginsLoaded) {
return <Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={true} className='w-full h-full flex items-center justify-center'></Spin>;
}
return (
<App className="h-full" message={{ maxCount: 1 }}>
<Router>
<Routes>
{generateRoutes(PUBLIC_ROUTES)}
</Routes>
</Router>
</App>
)
<RouterProvider router={router} />
);
}
const generateRoutes = (routerConfig: RouteConfig[]) => {
const generateRoutes = (routerConfig: RouteConfig[]):RouteObject[] => {
return routerConfig?.map((route: RouteConfig) => {
let routeElement;
if (route.lazy) {
const LazyComponent = route.lazy as React.ExoticComponent<unknown>;
let LazyComponent;
if (typeof route.lazy === 'function') {
const result = route.lazy();
if (result instanceof Promise) {
LazyComponent = React.lazy(() => result.then(module => ({ default: module.default || module })));
} else {
LazyComponent = result;
}
} else {
LazyComponent = route.lazy;
}
const GuardedComponent = route.lifecycle ? withRouteGuard(LazyComponent, {pathPrefix:`/${route.pathPrefix ?? route.path}`, ...route.lifecycle}): LazyComponent
routeElement = (
<Suspense fallback={ <div className=''><Skeleton className='m-btnbase w-calc-100vw-minus-padding-r' active /></div>}>
{route.provider ? (
createElement(route.provider, {}, <LazyComponent />)
createElement(route.provider, {}, <GuardedComponent />)
) : (
<LazyComponent />
<GuardedComponent />
)}
</Suspense>
);
} else {
routeElement = route.provider ? (
createElement(route.provider, {}, route.component)
createElement(route.provider, {}, route.component)
) : (
route.component
);
}
return (
<Route
key={route.key}
path={route.path}
element={routeElement}
>
{route.children && generateRoutes(route.children as RouteConfig[])}
</Route>
{
path: route.path,
element: <ErrorBoundary>{routeElement}</ErrorBoundary> ,
children: route.children ? generateRoutes(route.children as RouteConfig[]) : undefined,
exact:route?.pathMatch === 'full'
}
);
}
)
}
// 保护的路由组件
function ProtectedRoute() {
const {state} = useGlobalContext()
return state.isAuthenticated? <BasicLayout project="core" /> : <Navigate to="/login" />;
export function ProtectedRoute() {
// return state.isAuthenticated? <BasicLayout project="core" /> : <Navigate to="/login" />;
return <BasicLayout project="core" />
}
export default RenderRoutes
export default RenderRoutes
+533
View File
@@ -0,0 +1,533 @@
import component from '@common/components/aoplatform/prompt-editor/plugins/workflow-variable-block/component';
import { RouterMapConfig } from '@common/const/type';
import { ProtectedRoute } from '@core/components/aoplatform/RenderRoutes';
import { AiServiceProvider } from '@core/contexts/AiServiceContext';
import { SystemProvider } from '@core/contexts/SystemContext';
import { TeamProvider } from '@core/contexts/TeamContext';
import AiServiceOutlet from '@core/pages/aiService/AiServiceOutlet';
import Guide from '@core/pages/guide/Guide';
import Login from '@core/pages/Login';
import ServicePolicyLayout from '@core/pages/policy/ServicePolicyLayout';
import SystemOutlet from '@core/pages/system/SystemOutlet';
import { TenantManagementProvider } from '@market/contexts/TenantManagementContext';
import { lazy } from 'react';
import { Outlet, Navigate } from 'react-router-dom';
// 内置插件与对应组件/模块
export const routerMap:Map<string, RouterMapConfig> = new Map([
['basicLayout', { type: 'component', component: <ProtectedRoute />}],
['navHidden', { type: 'component', component: <ProtectedRoute /> }],
['login', { type: 'component', component: <Login /> }],
['guide',{
type:'component',
component:<Guide />
}],
['team', {type: 'module',
component:<Outlet/>,
key: 'team',
provider: TeamProvider,
children:[
{
path:'',
key: 'teamList',
component: <Navigate to="list" />
},
{
path:'list',
key: 'teamList2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamList.tsx'))
},
{
path:'inside/:teamId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamInsidePage.tsx')),
key: 'teamInside',
children:[
{
path:'member',
key: 'teamMember',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamInsideMember.tsx')),
},
{
path:'setting',
key: 'teamSetting',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamConfig.tsx')),
},
]
}
]
}],
['service', {
type: 'module',
path:'service',
component:<SystemOutlet />,
key: 'service',
provider: SystemProvider,
children:[
{
path:'',
key:'serviceList',
component:<Navigate to="list" />
},
{
path:'list',
key: 'serviceList2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemList.tsx')),
},
{
path:'list/:teamId',
key: 'serviceList3',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemList.tsx')),
},
{
path:':teamId',
component:<Outlet/>,
key: 'serviceInside',
children:[
{
path:'inside/:serviceId',
key: 'restServiceInside',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsidePage.tsx')),
children:[
{
path:'api',
key: 'restServiceInsideApi',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideApiDocument.tsx')),
},
{
path:'route/create',
key: 'restServiceInsideRouteCreate',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterCreate')),
},
{
path:'route/:routeId',
key: 'restServiceInsideRouteEdit',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterCreate')),
},
{
path:'route',
key: 'restServiceInsideRoute',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterList')),
},
{
path:'upstream',
key: 'restServiceInsideUpstream',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/upstream/SystemInsideUpstreamContent.tsx')),
},
{
path:'document',
key: 'restServiceInsideDocument',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsideDocument.tsx')),
},
{
path:'subscriber',
key: 'restServiceInsideSubscriber',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsideSubscriber.tsx')),
children:[
]
},
{
path:'approval',
key: 'restServiceInsideApproval',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApproval.tsx')),
children:[
{
path:'',
key: 'restServiceInsideApprovalList',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApprovalList.tsx')),
},
{
path:'*',
key: 'restServiceInsideApprovalList2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApprovalList.tsx')),
}
]
},
{
path:'publish',
key: 'systemPublish',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublish.tsx')),
children:[
{
path:'',
key: 'systemPublishList',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublishList.tsx')),
},
{
path:'*',
key: 'systemPublishList2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublishList.tsx')),
}
]
},
{
path:'setting',
key: 'systemConfig',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemConfig.tsx')),
children:[
]
},
{
path:'servicepolicy',
key: 'servicepolicy',
component:<ServicePolicyLayout />,
children:[{
path:'datamasking',
component:<Outlet />,
key:'dataMasking',
children:[
{
path:'list',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/servicePolicy')),
key:'dataMaskingList'
},
{
path:'create',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/dataMasking/DataMaskingConfig')),
key:'dataMaskingAdd'
},
{
path:':policyId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/dataMasking/DataMaskingConfig')),
key:'dataMaskingAdd'
}]
}
]
},
]
},
{
path:'aiInside/:serviceId',
component:<AiServiceOutlet />,
provider: AiServiceProvider,
key: 'aiServiceInside',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsidePage.tsx')),
children:[
{
path:'api',
key: 'aiServiceInsideApi',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideApiDocument')),
},
{
path:'route/create',
key: 'aiServiceInsideRouteCreate',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterCreate')),
},
{
path:'route/:routeId',
key: 'aiServiceInsideRouteEdit',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterCreate')),
},
{
path:'route',
key: 'aiServiceInsideRouteList',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterList')),
},
{
path:'document',
key: 'aiServiceInsideDocument',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsideDocument.tsx')),
},
{
path:'subscriber',
key: 'aiServiceInsideSubscriber',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsideSubscriber.tsx')),
children:[
]
},
{
path:'approval',
key: 'aiServiceInsideApproval',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApproval')),
children:[
{
path:'',
key: 'aiServiceInsideApprovalList',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApprovalList')),
},
{
path:'*',
key: 'aiServiceInsideApprovalList2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApprovalList')),
}
]
},
{
path:'publish',
key: 'aiServiceInsidePublish',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublish')),
children:[
{
path:'',
key: 'aiServiceInsidePublishList',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublishList')),
},
{
path:'*',
key: 'aiServiceInsidePublishList2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublishList')),
}
]
},
{
path:'setting',
key: 'aiServiceInsideSetting',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemConfig.tsx')),
children:[
]
},
{
path:'servicepolicy',
key: 'servicepolicy',
component:<ServicePolicyLayout />,
children:[{
path:'datamasking',
component:<Outlet />,
key:'dataMasking',
children:[
{
path:'list',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/servicePolicy')),
key:'dataMaskingList'
},
{
path:'create',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/dataMasking/DataMaskingConfig')),
key:'dataMaskingAdd'
},
{
path:':policyId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/dataMasking/DataMaskingConfig')),
key:'dataMaskingAdd'
}]
}
]
},
]
}
]
}
]
}],
['datasourcing', { type: 'component',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideDashboardSetting.tsx'))
}],
['cluster', { type: 'component',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideCluster.tsx')),
}],
['aisetting', { type: 'component',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiSetting/AiSettingList.tsx')),
}],
['cert', { type: 'component',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideCert.tsx')),
}],
['serviceHub', {
type: 'module',
component:<Outlet />,
key:'serviceHub',
children:[
{
path:'',
key: 'serviceHubList',
component: <Navigate to="list" />
},
{
path:'list',
key:'serviceHubList2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/ServiceHubList.tsx')),
},
{
path:'detail/:serviceId',
key:'serviceHubDetail',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/ServiceHubDetail.tsx')),
}]
}],
['commonsetting', { type: 'module',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/common/CommonPage.tsx')),
}],
['consumer', { type: 'module',
component:<Outlet />,
provider:TenantManagementProvider,
key:'consumer',
children:[
{
path:'',
key:'consumerList',
component:<Navigate to="list" />
},
{
path:':teamId/inside/:appId',
key:'consumerInside',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsidePage.tsx')),
children:[
{
path:'service',
key:'consumerInsideService',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsideService.tsx')),
},
{
path:'authorization',
key:'consumerInsideAuthorization',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsideAuth.tsx')),
},
{
path:'setting',
key:'consumerSetting',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementAppSetting.tsx')),
},
]
},
{
path:'list',
key:'serviceHubManagementList',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ServiceHubManagement.tsx')),
},
{
path:'list/:teamId',
key:'serviceHubManagementList2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ServiceHubManagement.tsx')),
},
]}],
['member', { type: 'module',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberPage.tsx')),
children:[
{
path:'',
key:'memberList',
component:<Navigate to="list" />
},
{
path:'list',
key:'memberList2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberList.tsx')),
},
{
path:'list/:memberGroupId',
key:'memberList3',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberList.tsx')),
}
],
}],
['role', { type: 'module',
component:<Outlet></Outlet>,
children:[
{
path: '',
key: 'roleList',
component: <Navigate to="list" />
},
{
path:'list',
key:'roleList2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleList.tsx')),
},
{
path:':roleType/config/:roleId',
key:'roleConfig',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleConfig.tsx')),
},
{
path:':roleType/config',
key:'roleConfig2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleConfig.tsx')),
}
]
}],
['analytics', { type: 'module',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/Dashboard.tsx')),
key:'analytics',
children:[
{
path:'total',
key:'analytics2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardTotal.tsx')),
},
{
path:':dashboardType',
key:'analytics3',
component:<Outlet />,
children:[
{
path:'list',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardList.tsx')),
key:'analyticsList'
},
{
path:'detail/:dashboardDetailId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardDetail.tsx')),
key:'analyticsDetail'
},
]
}
]
}],
['template', { type: 'module',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
}],
['logsettings', { type: 'module',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/logsettings/LogSettings.tsx')),
key:'logsettings',
children:[
{
path:'template/:moduleId',
key:'logSettings2',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
},
]
}],
['resourcesettings', { type: 'module',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/resourcesettings/ResourceSettings.tsx')),
key:'resourcesettings',
children:[
{
path:'template/:moduleId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
key:'resourceSettings2'
},
]
}],
['userProfile', { type: 'module',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/UserProfile.tsx')),
key:'userProfile',
children:[{
path:'changepsw',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/ChangePsw.tsx')),
key:'changePsw'
}]}],
['globalPolicy', { type: 'module',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/GlobalPolicyLayout')),
key:'globalPolicy',
children:[{
path:'datamasking',
component:<Outlet />,
key:'dataMasking',
children:[
{
path:'list',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/globalPolicy')),
key:'dataMaskingList'
},
{
path:'create',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/dataMasking/DataMaskingConfig')),
key:'dataMaskingAdd'
},
{
path:':policyId',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/policy/dataMasking/DataMaskingConfig')),
key:'dataMaskingAdd'
}]
}]
}],
])
@@ -35,6 +35,7 @@ export type MemberDropdownModalFieldType = {
parent?:string
email?:string
departmentIds?:string[]
password:string
};
export type MemberDropdownModalProps = {
@@ -1,115 +1,157 @@
import { PageProColumns } from "@common/components/aoplatform/PageList";
import { COLUMNS_TITLE } from "@common/const/const";
import { EntityItem } from "@common/const/type";
export type PartitionConfigFieldType = {
name?: string;
id?: string;
description?: string;
prefix?:string
url?:string
managerAddress?:string
canDelete?:boolean
name?: string;
id?: string;
description?: string;
prefix?: string
url?: string
managerAddress?: string
canDelete?: boolean
};
export type PartitionCertTableListItem = {
id:string;
name: string;
domains:string[];
notAfter:string;
notBefore:string;
updater:EntityItem;
updateTime:string;
id: string;
name: string;
domains: string[];
notAfter: string;
notBefore: string;
updater: EntityItem;
updateTime: string;
};
export type PartitionCertConfigFieldType = {
id?:string
key:string
pem:string
id?: string
key: string
pem: string
};
export type PartitionCertConfigProps = {
type:'add'|'edit'
entity?:PartitionCertConfigFieldType
type: 'add' | 'edit'
entity?: PartitionCertConfigFieldType
}
export type PartitionCertConfigHandle = {
save:()=>Promise<boolean|string>
save: () => Promise<boolean | string>
}
export type PartitionClusterFieldType = {
name?: string;
id?: string;
description?: string;
address?:string;
protocol?:'http'|'https'
name?: string;
id?: string;
description?: string;
address?: string;
protocol?: 'http' | 'https'
};
export type ClusterConfigProps = {
mode:'config' | 'retry' | 'result' | 'edit',
clusterId?:string
initFormValue?:{[k:string]:string|number}
mode: 'config' | 'retry' | 'result' | 'edit',
clusterId?: string
initFormValue?: { [k: string]: string | number }
}
export type ClusterConfigHandle = {
save:()=>Promise<boolean|string>
check:()=>Promise<boolean>
save: () => Promise<boolean | string>
check: () => Promise<boolean>
}
export type PartitionClusterTableListItem = {
id:string;
name: string;
status:0|1;
description:string;
id: string;
name: string;
status: 0 | 1;
description: string;
};
export type PartitionClusterNodeTableListItem = {
id:string;
name: string;
managerAddress:string[];
serviceAddress:string[];
peerAddress:string[];
status:0|1;
id: string;
name: string;
managerAddress: string[];
serviceAddress: string[];
peerAddress: string[];
status: 0 | 1;
};
export type PartitionClusterNodeModalTableListItem = {
id: string,
name: string,
managerAddress: [],
serviceAddress: [],
peerAddress: string,
status: string
id: string,
name: string,
managerAddress: [],
serviceAddress: [],
peerAddress: string,
status: string
}
export type NodeModalFieldType = {
address:string
address: string
}
export type NodeModalHandle = {
save:()=>void
save: () => void
}
export type NodeModalPropsType = {
changeStatus:(status:ClusterPageShowStatus)=>void
getClusterInfo:()=>void
status:ClusterPageShowStatus
changeStatus: (status: ClusterPageShowStatus) => void
getClusterInfo: () => void
status: ClusterPageShowStatus
}
export type ClusterPageShowStatus = 'view'|'preview'|'edit'
export type ClusterPageShowStatus = 'view' | 'preview' | 'edit'
export type PartitionTableListItem = {
id:string;
name: string;
clusterNum:number;
updater:EntityItem;
updateTime:string;
id: string;
name: string;
clusterNum: number;
updater: EntityItem;
updateTime: string;
};
export type SimplePartition = EntityItem & { clusters: (EntityItem & {description:string})[] }
export type SimplePartition = EntityItem & { clusters: (EntityItem & { description: string })[] }
export type PartitionDashboardConfigFieldType = {
driver:string
config:{
org:string
token:string
addr:string
driver: string
config: {
org: string
token: string
addr: string
}
}
export type PartitionDataLogHeaderListFieldType = {
key: string
value: string
}
export type PartitionDataLogConfigFieldType = {
headers: PartitionDataLogHeaderListFieldType[]
url: string
}
export const PARTITION_DATA_LOG_CONFIG_TABLE_COLUMNS: PageProColumns<PartitionDataLogConfigFieldType & { _id: string }>[] = [
{
title: ('标签'),
dataIndex: 'key',
formItemProps: {
className: 'p-0 bg-transparent border-none',
rootClassName: 'test',
rules: [
{
required: true,
whitespace: true
},
],
},
ellipsis: true
},
{
title: ('内容'),
dataIndex: 'value',
formItemProps: {
className: 'p-0 bg-transparent border-none'
}
}
},
{
title: COLUMNS_TITLE.operate,
valueType: 'option',
btnNums: 2,
render: () => null
},
];
@@ -3,9 +3,10 @@ import { Input, TabsProps } from "antd";
import { MatchItem } from "@common/const/type";
import { ConfigField } from "@common/components/aoplatform/EditableTableWithModal";
import { frontendTimeSorter } from "@common/utils/dataTransfer";
import { COLUMNS_TITLE } from "@common/const/const";
import { COLUMNS_TITLE, PLACEHOLDER } from "@common/const/const";
import { PageProColumns } from "@common/components/aoplatform/PageList";
import { $t } from "@common/locales";
export enum SubscribeEnum{
Rejected = 0,
@@ -222,7 +223,7 @@ export const MATCH_CONFIG:ConfigField<MatchItem>[] = [
}, {
title:('参数名'),
key: 'key',
component: <Input className="w-INPUT_NORMAL" />,
component: <Input className="w-INPUT_NORMAL"/>,
renderText: (value: unknown) => value,
required: true
}, {
@@ -236,7 +237,7 @@ export const MATCH_CONFIG:ConfigField<MatchItem>[] = [
title:('参数值'),
key: 'pattern',
unRender:(formValue)=>{return formValue?.matchType === 'NULL' || formValue?.matchType==='EXIST' || formValue?.matchType === 'UNEXIST'},
component: <Input className="w-INPUT_NORMAL"/>,
component: <Input className="w-INPUT_NORMAL" />,
renderText: (value: string) => {
return value
},
@@ -1,23 +0,0 @@
import {FC, createContext, useContext, useState, ReactNode } from 'react';
import { PartitionConfigFieldType } from '../const/partitions/types.ts';
interface PartitionContextProps {
partitionInfo:PartitionConfigFieldType|undefined
setPartitionInfo:React.Dispatch<React.SetStateAction<PartitionConfigFieldType|undefined>>;
}
const PartitionContext = createContext<PartitionContextProps | undefined>(undefined);
export const usePartitionContext = () => {
const context = useContext(PartitionContext);
if (!context) {
throw new Error('useArray must be used within a ArrayProvider');
}
return context;
};
export const PartitionProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [partitionInfo, setPartitionInfo] = useState<PartitionConfigFieldType>()
return <PartitionContext.Provider value={{ partitionInfo, setPartitionInfo }}>{children}</PartitionContext.Provider>;
};
@@ -1,23 +0,0 @@
import { createContext, useContext, useState, ReactNode, FC } from 'react';
import { MyServiceFieldType } from '../const/system/type.ts';
interface SystemMyServiceContextProps {
serviceInfo:MyServiceFieldType|undefined
setServiceInfo:React.Dispatch<React.SetStateAction<MyServiceFieldType|undefined>>;
}
const SystemMyServiceContext = createContext<SystemMyServiceContextProps | undefined>(undefined);
export const useSystemMyServiceContext = () => {
const context = useContext(SystemMyServiceContext);
if (!context) {
throw new Error('useArray must be used within a ArrayProvider');
}
return context;
};
export const SystemMyServiceProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [serviceInfo, setServiceInfo] = useState<MyServiceFieldType>()
return <SystemMyServiceContext.Provider value={{ serviceInfo, setServiceInfo }}>{children}</SystemMyServiceContext.Provider>;
};
+21 -3
View File
@@ -321,6 +321,11 @@ p{
}
}
.ant-table-body {
scrollbar-width: auto;
scrollbar-color: auto;
}
::-webkit-scrollbar {
width: 10px;
@@ -664,7 +669,7 @@ p{
.ant-pro-table-list-toolbar-setting-items{
position:absolute;
top:18px;
right:16px;
right:22px;
z-index:9;
.ant-pro-table-list-toolbar-setting-item{
font-size:14px;
@@ -970,7 +975,7 @@ p{
.ant-drawer-content-wrapper{
min-width: 820px !important;
.ant-table:not(.ant-table-bordered){
border:1px solid var(--border-color) !important;
/* border:1px solid var(--border-color) !important; */
border-top:0px !important;
}
.ant-table:not(.ant-table-borderer){
@@ -996,7 +1001,7 @@ p{
.ant-modal-wrap:not(.height-fixed-modal){
.ant-modal-body{
.ant-table:not(.ant-table-bordered){
border:1px solid var(--border-color) !important;
/* border:1px solid var(--border-color) !important; */
border-top:0px !important;
}
.ant-table:not(.ant-table-borderer){
@@ -1005,6 +1010,19 @@ p{
}
}
.global-policy-tabs {
.ant-tabs-nav {
&::before {
border-bottom: none;
}
}
.ant-tabs-content {
height: 100%;
.ant-tabs-tabpane {
height: 100%;
}
}
}
.ant-tooltip{
max-width: 280px !important;

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