Compare commits

..

152 Commits

Author SHA1 Message Date
Hongchao Deng
a10dccf5bd Vela APIServer and Catalog Design (#881)
* APIServer + Catalog Architecture Design

Signed-off-by: Hongchao Deng <hongchaodeng1@gmail.com>

* update
2021-01-15 11:22:59 -08:00
Jianbo Sun
f40a41dbc1 Merge pull request #888 from resouer/fix-msg
Add more details for platform builder
2021-01-15 19:51:28 +08:00
Jianbo Sun
4732bd943d Update docs/en/concepts.md
Co-authored-by: Zheng Xi Zhou <zzxwill@gmail.com>
2021-01-15 19:05:40 +08:00
Jianbo Sun
89863b30e8 fix ci and refactor code (#887)
* adjust CI scripts

* fix go ci order

* try fix

* use appfile to update trait

* fix vela status

* use assert.ObjectAreEqual to check diff for map case

* remove application controller own AC and Components

* tune place

* fix lint

* update

* remove error check of vela init test

Co-authored-by: Ryan Zhang <yangzhangrice@hotmail.com>
2021-01-15 19:04:12 +08:00
Harry Zhang
00ba0697dc Update concepts and readme to reflect recent change 2021-01-14 22:14:57 -08:00
Harry Zhang
30585f80e6 Init platform builder doc 2021-01-14 21:38:03 -08:00
Zheng Xi Zhou
83ce5ec724 Add ConflictsWith and more sections for Rollout (#869)
Added `ConflictsWith` and `How Rollout Works` section to Rollout Reference Docs.
To fix #777
2021-01-14 17:22:55 +08:00
Jianbo Sun
4467084c0e Merge pull request #875 from wonderflow/vela-up
add tests for application controller
2021-01-11 21:23:41 +08:00
天元
1f1f3af7b7 refacter and add tests 2021-01-11 20:48:17 +08:00
Ryan Zhang
d38c08af56 Merge pull request #846 from leejanee/vela-up
vela up generate application object instead of AC and Component
2021-01-10 22:10:39 -08:00
天元
2761e7afb9 use map[string]string for scope 2021-01-11 10:54:57 +08:00
天元
8410f03eb0 addressing comments 2021-01-11 10:45:13 +08:00
Jianbo Sun
e51c283c1d Merge pull request #868 from zzxwill/apischema
Add unit-test for generateOpenAPISchema
2021-01-09 21:48:33 +08:00
天元
f694d1ad5d add tests 2021-01-08 18:16:03 +08:00
zzxwill
674408784b address comments and add one more testcase 2021-01-08 15:46:30 +08:00
majian
19e8aa2af6 Dashboard fix (#871)
* 1.fix repeated click does not show component bug
2.typed
3.fix locales

* capability rename

Co-authored-by: Zheng Xi Zhou <zzxwill@gmail.com>
2021-01-08 10:54:15 +08:00
天元
3dd954541e add tests 2021-01-07 20:24:25 +08:00
天元
ba614da809 fix ci 2021-01-07 19:40:32 +08:00
天元
b92e2a92fc try test again 2021-01-07 19:28:19 +08:00
天元
718a4b1999 fix ci 2021-01-07 17:52:15 +08:00
天元
de83320306 remove unused code 2021-01-07 13:07:28 +08:00
天元
4cfee561b9 refactor and add scope to application 2021-01-07 13:07:12 +08:00
Jianbo Sun
6b78945c18 Merge pull request #858 from Incubator4th/route-provider
Add provider parameter to route trait
2021-01-07 11:49:41 +08:00
incubator4th
72d6981628 set provider can be empty 2021-01-06 17:22:16 +08:00
zzxwill
2243632d0c Add unit-test for generateOpenAPISchema
Added title for all fields and added unit-test for generateOpenAPISchema FixOpenSchema,
2021-01-06 14:36:36 +08:00
Jianbo Sun
775991cfaa Merge pull request #784 from zzxwill/form-render
Implement API for generating OpenAPI schema for definition
2021-01-06 11:58:20 +08:00
Zheng Xi Zhou
da097989f8 Update pkg/appfile/service.go
Co-authored-by: Hongchao Deng <hongchaodeng1@gmail.com>
2021-01-06 11:05:37 +08:00
zzxwill
53164a4d2f Implement API for generating OpenAPI schema for definition
Fetched cue parameters from workloaddefinition/traitdefinition, generated
OpenAPI v3 schema, fixed problems of tainted `description` filed, added filed
"title" required by form-render component, added it in API server router

To fix #680
2021-01-05 21:57:38 +08:00
天元
3d3b3239d8 add trait type 2021-01-05 21:18:27 +08:00
天元
b1a97b08ec add workload type to label 2021-01-05 21:08:41 +08:00
incubator4th
72663f68cb add provider to route.cue 2021-01-05 21:06:55 +08:00
天元
8c7db9b0e0 larger time 2021-01-05 19:31:11 +08:00
天元
c72941f916 check application 2021-01-05 19:18:48 +08:00
天元
cc590d7c42 using vela up to deploy 2021-01-05 18:10:22 +08:00
天元
5e06e41477 try fix ci 2021-01-05 17:15:55 +08:00
天元
6e658d1ea8 Merge branch 'master' into vela-up 2021-01-05 17:15:03 +08:00
Jianbo Sun
4832d95506 Merge pull request #854 from wonderflow/contribute
refine contributing doc
2021-01-05 13:33:46 +08:00
Jianbo Sun
61b9816296 Merge pull request #853 from wonderflow/tests
add test for component revision hook
2021-01-05 12:09:57 +08:00
天元
9a613da8c5 refine contributing doc 2021-01-05 12:09:23 +08:00
guoxudong
72ee5db872 Fix `each child in a list should have a unique "key" prop.' (#844)
* fix capability

* fix
2021-01-04 13:30:59 -08:00
lj176172
cb68136f93 remove 2021-01-04 17:26:34 +08:00
lj176172
a181ff46b6 extend exec-timeout period 2021-01-04 16:28:38 +08:00
lj176172
2e1433c366 extend exec-timeout period 2021-01-04 16:04:54 +08:00
lj176172
bac35a425f extend exec-timeout period 2021-01-04 15:53:03 +08:00
lj176172
08d0b4830a recover 2020-12-31 18:41:46 +08:00
lj176172
a1d9b3d032 remove trait attach 2020-12-31 18:31:24 +08:00
Jianbo Sun
8d0595ed4e Update cmd/core/main.go 2020-12-31 17:13:13 +08:00
天元
01bd05d0a5 add test for component revision hook 2020-12-31 16:41:55 +08:00
lj176172
76d585795b fix ls command 2020-12-31 16:18:37 +08:00
lj176172
4088a20252 ApplyApp 2020-12-31 12:26:05 +08:00
Jianbo Sun
810cbcaae4 Merge pull request #841 from zzxwill/issue-trait-no-definition
Allow trait to work without TraitDefinition
2020-12-31 10:52:01 +08:00
zzxwill
dfdb833abe Check trait CR is created and update ApplicationConfiguration 2020-12-31 09:16:56 +08:00
Jianbo Sun
f10e53c1eb Merge pull request #847 from wonderflow/application
refine error message: trait definition not found
2020-12-30 19:01:05 +08:00
lj176172
a88d0a5660 lint code 2020-12-30 18:26:31 +08:00
天元
02bb9a1dc1 refine error message: trait definition not found 2020-12-30 18:25:30 +08:00
lj176172
950238aa11 export scopes 2020-12-30 18:19:38 +08:00
lj176172
b52fc09fd6 lint code 2020-12-30 14:13:27 +08:00
lj176172
062c906e95 rebase master 2020-12-30 14:08:54 +08:00
Jianbo Sun
69e860a1e8 Merge pull request #843 from zzxwill/dashboard-start
Refine Dashboard README
2020-12-30 13:11:54 +08:00
zzxwill
0bd85d359a Refine Dashboard README
Refined dashboard readme with all-in-one start command
2020-12-30 11:06:51 +08:00
Jianbo Sun
1abbe1f7cf Merge pull request #842 from ryanzhang-oss/minor-improve
Use the correct cue parse api
2020-12-30 10:19:06 +08:00
Jianbo Sun
88143ae64a Merge pull request #834 from zzxwill/workload
Add description field for Capability/Workloads list
2020-12-30 10:08:13 +08:00
Ryan Zhang
b25e911a74 use the correct cue parse api 2020-12-29 13:24:04 -08:00
zzxwill
d8d891f6c9 Allow trait to work without TraitDefinition
Fix the issue of applying trait if its traitdefinition
doesn't exits.

To fix issue #839
2020-12-30 00:14:26 +08:00
Jianbo Sun
a7f823940c Merge pull request #832 from wonderflow/application
refactor application object to have basic schema
2020-12-29 17:11:35 +08:00
天元
3a47d5de73 refactor application object to have more strict schema 2020-12-29 15:48:32 +08:00
zzxwill
eb2501f644 fix lint issue 2020-12-29 10:58:07 +08:00
zzxwill
abce944aad Add description field for Capability/Workloads list
Added `description` field for workloads.

To fix #821
2020-12-29 10:43:05 +08:00
Jianbo Sun
386d5dd0ee Merge pull request #833 from zzxwill/workload
Expend column width for "vela workloads/traits"
2020-12-28 22:00:59 +08:00
zzxwill
509895511b Expend column width for "vela workloads/traits"
Expended column width for `vela workloads/traits` and also
shortened the description for all workloads and traits.
Fixed a tiny issue in hack/reference/generate.go

To fix issue #827
2020-12-28 20:57:22 +08:00
Weiping Cai
c0ebe8dc62 support mutli storage resources,define storage interface and implemen… (#776)
* support mutli storage resources,define storage interface and implement local storage

Signed-off-by: Weiping Cai <weiping.cai@daocloud.io>

* support mutli storage resources,define storage interface and implement local storage

Signed-off-by: Weiping Cai <weiping.cai@daocloud.io>

* support mutli storage resources,define storage interface and implement local storage

Signed-off-by: Weiping Cai <weiping.cai@daocloud.io>

* support mutli storage resources,define storage interface and implement local storage

Signed-off-by: Weiping Cai <weiping.cai@daocloud.io>

* move application/Application to storage/driver pkg

Signed-off-by: Weiping Cai <weiping.cai@daocloud.io>

* update storage import format

Signed-off-by: Weiping Cai <weiping.cai@daocloud.io>
2020-12-28 18:39:58 +08:00
Jianbo Sun
ebf1b51d58 Merge pull request #831 from zzxwill/mutation-webhook-conflicts
Add "How to fix issue: MutatingWebhookConfiguration mutating-webhook-configuration exists?"
2020-12-28 18:39:17 +08:00
zzxwill
4b27f9b4e2 Add "How to fix issue: MutatingWebhookConfiguration mutating-webhook-configuration exists?"
Added another question.

To cover issue #794
2020-12-28 17:44:58 +08:00
Jianbo Sun
8f6d0fded8 Merge pull request #825 from wonderflow/fix1
run CI check for all PR including docs/ and *md
2020-12-28 13:38:41 +08:00
Jianbo Sun
e8cabdc13c Merge pull request #824 from zzxwill/issue-workloads
Add "description" field for each workload from ListWorkloads API
2020-12-28 10:02:01 +08:00
Jianbo Sun
a03d9f2626 Merge pull request #828 from zzxwill/issue-vela-workloads
Refine error message for trait without `spec.extension.template`
2020-12-27 11:06:50 +08:00
zzxwill
37cb5ccb51 Add "description" field for each workload from ListWorkloads API
Workload item doesn't have descriptin property, added it to help display
the description in capability list on Dashboard
2020-12-25 19:04:54 +08:00
zzxwill
4c34048186 Refine error message for trait without spec.extension.template
Error message of trait without `spec.extension.template`
adheres with normal message when executing "vela workloads".

To fix #826
2020-12-25 18:25:19 +08:00
天元
76e0bbcabd run CI check for all PR including docs/ and *md 2020-12-25 17:53:04 +08:00
Jianbo Sun
32bc74456a Merge pull request #810 from Cweiping/fix/fix_promethus_typo_error_2
fix typo error of promethus
2020-12-25 17:39:22 +08:00
Jianbo Sun
82df7c3a71 Merge pull request #811 from captainroy-hy/fix-integration-test
fix unstable integration test
2020-12-25 17:38:21 +08:00
majian
c06aca6f60 add dashboard lint check (#815) and remove unused ref 2020-12-25 14:24:06 +08:00
Jianbo Sun
69dcf2fc5a Merge pull request #807 from wonderflow/datainput
support strategy patch datainput merge for slice case
2020-12-25 12:42:13 +08:00
Yang Liu
04c9af5c81 Merge pull request #812 from wonderflow/revision
support custom component revision hook
2020-12-25 11:51:02 +08:00
guoxudong
1e6248d189 [Dashboard] Capabilities page (#803)
* add capabilities (workloads and traits)

* yarn prettier
2020-12-25 11:13:08 +08:00
天元
e3b4bcd2ca support custom component revision 2020-12-25 09:40:54 +08:00
roywang
9730cb438d fix unstable integration test
Signed-off-by: roywang <seiwy2010@gmail.com>
2020-12-24 18:22:27 +09:00
Jianbo Sun
a8f48eb140 Merge pull request #809 from hongchaodeng/owner
add sunny0826 (Xudong Guo) as reviewer
2020-12-24 14:20:59 +08:00
Weiping Cai
ab6a170c1d fix typo error of promethus
Signed-off-by: Weiping Cai <weiping.cai@daocloud.io>
2020-12-24 14:00:17 +08:00
Hongchao Deng
dc9b1eb6fc add sunny0826 (Xudong Guo) as reviewer 2020-12-23 21:55:04 -08:00
Hongchao Deng
629bf00b63 update owner file (#808)
Signed-off-by: Hongchao Deng <hongchaodeng1@gmail.com>
2020-12-23 21:51:31 -08:00
Jian.Li
1f2c38d3c7 supports strategy patch for definition template (#798)
* supports strategy patch for definition template

* strategy unify

* lint code

* test cases

* add docs

* add test cases

* lint code

* add test cases for walk

* fix close fun

* lint code

* const variable for patchKey

* lint code

* interceptor for ast.Node

* add test cases

* check diff

* strategyUnify

* listMergeByKey

* prompts
2020-12-24 13:35:34 +08:00
Hongchao Deng
b129637cf2 add Community doc to clarify membership ladder (#805)
* add Community doc to clarify membership ladder

* update
2020-12-23 11:07:06 -08:00
Yue Wang
630f1e48bc enable webhooks for AppConfig/Comp (#781)
rename webhooks

fix e2e tests

Signed-off-by: roywang <seiwy2010@gmail.com>
2020-12-23 11:02:06 -08:00
天元
4bed55b400 support strategy patch datainput merge for slice case 2020-12-23 21:53:05 +08:00
Jianbo Sun
54ca5c115f Merge pull request #774 from captainroy-hy/trait-conflict-validation
implement conflictsWtih feature in trait definition
2020-12-23 13:32:38 +08:00
Zheng Xi Zhou
ddba62fba0 Generate restful api based on Swagger (#765)
* Generate restful api based on Swagger

As OpenAPI has to be quickly developped to support front-end developement, so
did the restufl api docs. Based on swagger, generate the docs automatically

* fix conflicts and add application list annotation
2020-12-22 16:35:26 -08:00
guoxudong
15a24c65b9 fix toc (#802) 2020-12-22 16:32:48 -08:00
guoxudong
56285ca540 add restful api docs (#801) 2020-12-22 15:16:56 +08:00
Jianbo Sun
b1c3f593db Merge pull request #800 from sunny0826/brew-install
docs add brew
2020-12-22 14:53:45 +08:00
guoxudong
e23f6b3866 docs add brew 2020-12-22 14:47:54 +08:00
Jianbo Sun
2943bc7ce1 Merge pull request #797 from wonderflow/fixrevision
fix workload reconcile error when revision enable trait is attached
2020-12-21 22:27:47 +08:00
Jianbo Sun
da0f8916aa Merge pull request #790 from zzxwill/issue-vela-install
Refine error message for vela installation
2020-12-21 21:15:21 +08:00
天元
7609da212f fix workload can not be update when revision enable trait is attached 2020-12-21 21:12:19 +08:00
Zheng Xi Zhou
7f87a47832 Update pkg/commands/system.go
Co-authored-by: Jianbo Sun <wonderflow.sun@gmail.com>
2020-12-21 18:05:42 +08:00
Jianbo Sun
04868d217b Merge pull request #791 from zzxwill/autoscale-metrics-server
Add "Autoscale: how to enable metrics server in various Kubernetes clusters?" in FAQ
2020-12-21 14:35:42 +08:00
zzxwill
56aa3c24d7 Add "Autoscale: how to enable metrics server in various Kubernetes clusters?" in FAQ
Also linked the FAQ in set-autoscale.md
2020-12-18 21:02:11 +08:00
zzxwill
a54ba9bb54 Refine error message for vela installation
Refined error message for vela installation and increase
timeout to 5 minutes from 2 minutes. And did some code
refactoring

To fix issue #783
2020-12-18 19:41:38 +08:00
Jianbo Sun
fbe93dc847 Merge pull request #771 from wonderflow/blog
add blog about how to extend kubevela by cuelang
2020-12-18 19:33:59 +08:00
天元
dbd855cc25 address comments 2020-12-18 19:11:57 +08:00
Hongchao Deng
7f136159c0 fix inconsistency for dev proxy config (#786) 2020-12-17 20:39:27 -08:00
Hongchao Deng
af61b59c5e return go time in Format RFC3339 to match js Date (#785)
Signed-off-by: Hongchao Deng <hongchaodeng1@gmail.com>
2020-12-17 20:11:18 -08:00
majian
c09e4df452 1.add application list page (#779)
2.ignore the deleted applicationconfig resource instead return nil
3.code style fix
2020-12-17 09:22:38 -08:00
Jianbo Sun
1694980eba Update docs/blog/zh/extend-kubevela-by-cuelang-in-20-mins.md
Co-authored-by: Lei Zhang (Harry) <resouer@gmail.com>
2020-12-17 09:54:46 +08:00
Jianbo Sun
feee1306fd Update docs/blog/zh/extend-kubevela-by-cuelang-in-20-mins.md
Co-authored-by: Lei Zhang (Harry) <resouer@gmail.com>
2020-12-17 09:54:30 +08:00
Jianbo Sun
0d56da4ab8 Update docs/blog/zh/extend-kubevela-by-cuelang-in-20-mins.md
Co-authored-by: Lei Zhang (Harry) <resouer@gmail.com>
2020-12-17 09:53:28 +08:00
roywang
582c3788c4 implement TraitConflict check in webhook
add unit tests

Signed-off-by: roy wang <seiwy2010@gmail.com>
2020-12-16 18:01:44 +09:00
天元
b8fd6f9823 add blog about how to extend kubevela by cuelang 2020-12-15 20:14:50 +08:00
Jianbo Sun
0fab691ba3 Merge pull request #773 from wonderflow/fiximports
minor fix
2020-12-15 15:56:23 +08:00
天元
3cb172f7ff minor fix 2020-12-15 13:55:36 +08:00
Jian.Li
e882a650ae Extend application's capabilities by trait definition (#742)
* add dsl pkg

* realize context capabilities for application

* fix golint

* fix check-diff

* upgrade vela server sample

* comment group

* Definition Reference be Optional & fix  three-part trait crd apply without namespace

* Improve samples
2020-12-13 20:00:06 -08:00
Jianbo Sun
ef9c26ffec Merge pull request #686 from majian159/hotfix-notfound-error
application not found return error. fix #685
2020-12-14 10:41:14 +08:00
majian
cf2a57c96a Integrate swagger and add env api (#764)
* 1.swagger integrate
2.add env swagger comment

Signed-off-by: majian <majian159@live.com>

* use swagger.json

Signed-off-by: majian <majian159@live.com>
2020-12-12 08:54:33 -08:00
majian
7acc52ebdb 1.use yarn (#769)
2.reinstall package

Signed-off-by: majian <majian159@live.com>
2020-12-11 10:28:08 -08:00
Jianbo Sun
3c35d9dadc Merge pull request #684 from majian159/feature-exec-svc
add svc flag for exec command.
2020-12-11 20:53:41 +08:00
majian
07742b6349 application not found return error. fix #685 and fix unit tests
Signed-off-by: majian <majian159@live.com>
2020-12-11 19:54:18 +08:00
majian
d45d086efb add svc flag for exec command and add unit test
Signed-off-by: majian <majian159@live.com>
2020-12-11 19:32:50 +08:00
Jianbo Sun
a31a316171 Merge pull request #767 from sunny0826/brew
Adapted for brew installation
2020-12-11 13:54:50 +08:00
guoxudong
ec287a181f Adapted for brew installation 2020-12-11 10:20:53 +08:00
Zheng Xi Zhou
50f4d10b00 Add make target to start both OpenAPI server and dashboard (#766)
Start up dashboard all in one
2020-12-10 11:10:10 -08:00
majian
9f27f70da8 1.migrate-env (#757)
2.add lockfile to version control
3.update packages
2020-12-10 15:05:33 +08:00
Jianbo Sun
3df55a92f3 Merge pull request #762 from resouer/dev
Update slack channel link
2020-12-10 12:08:04 +08:00
Harry Zhang
fd65c27ecb Update slack channel link 2020-12-09 19:49:15 -08:00
Jianbo Sun
576d928a83 Merge pull request #761 from captainroy-hy/fix-golint-1
fix golint issues
2020-12-10 11:39:20 +08:00
roywang
d14558a227 fix golint issues
Signed-off-by: roywang <seiwy2010@gmail.com>
2020-12-10 12:10:52 +09:00
Jianbo Sun
f48aba6f66 Merge pull request #743 from captainroy-hy/faster-refresh
faster refresh capabilities
2020-12-10 10:16:52 +08:00
Zheng Xi Zhou
f51dccf956 Minor updates on docs (#758)
Updated reference docs for autoscale and task, updated
cli docs
2020-12-09 10:35:59 -08:00
roywang
a961b002c2 faster refresh capabilities
Signed-off-by: roywang <seiwy2010@gmail.com>
2020-12-09 23:51:04 +09:00
Jianbo Sun
bffd43fa2c Merge pull request #754 from zzxwill/disscussion
Remove "Question" issue template
2020-12-09 22:14:47 +08:00
Jianbo Sun
2a88f9221f Merge pull request #728 from captainroy-hy/fix-uitable-wrap
fix cmd uitable wrap issue
2020-12-09 22:14:05 +08:00
Jianbo Sun
efdef27d0a Merge pull request #756 from zzxwill/extending-docs
Minor docs fix
2020-12-09 22:12:10 +08:00
roy wang
58bef9791f fix cmd uitable wrap issue
Signed-off-by: roy wang <seiwy2010@gmail.com>
2020-12-09 19:48:17 +09:00
zzxwill
c1de71d122 Minor docs fix
In extending trait docs, correct heading level for "Step 3"
2020-12-09 17:06:14 +08:00
majian
f8e320b97c Feature dashboard scaffold init. (#747)
* ant design pro5 init.

* scaffold init

Signed-off-by: majian <majian159@live.com>
2020-12-09 16:48:48 +08:00
zzxwill
f69476df37 Remove "question" issue template
Removed "Question" issue as we enable Github "Discussion" #744
2020-12-09 14:50:29 +08:00
Jianbo Sun
922892a909 Merge pull request #753 from zzxwill/faq
Add `what's the difference between Kubevela and helm` in FAQ
2020-12-09 14:35:43 +08:00
zzxwill
f36f7296fd Add what's the difference between Kubevela and helm in FAQ
Added the question from #704 in FAQ
2020-12-09 14:32:30 +08:00
Jianbo Sun
eff9d5f6e9 Merge pull request #752 from ElementakGod/fix-rds
Fixed the problem that the rds crd name is incorrect
2020-12-09 13:55:13 +08:00
Jianbo Sun
a75f32857a Merge pull request #750 from sunny0826/docs
Toc adapts to narrow screens
2020-12-09 13:41:19 +08:00
guoxudong
f011fe14bc fix 2020-12-09 13:40:04 +08:00
陈浩
7889a8c467 change the name of rds crd 2020-12-09 13:37:48 +08:00
guoxudong
f37cb3a1a7 Toc adapts to narrow screens 2020-12-09 13:01:35 +08:00
437 changed files with 38757 additions and 10221 deletions

View File

@@ -1,12 +0,0 @@
---
name: Question
about: Help wanted.
title: "[Question]"
labels: help wanted
assignees: ''
---
<!--
Please write your questions.
-->

View File

@@ -5,9 +5,6 @@ on:
branches: [ master ]
pull_request:
branches: [master]
paths-ignore:
- 'docs/**'
- '**.md'
defaults:
run:
working-directory: ./dashboard
@@ -16,7 +13,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
node-version: [10.x]
node-version: [12.x]
steps:
- uses: actions/checkout@v2
@@ -24,6 +21,7 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run build
- run: yarn
- run: yarn lint
- run: yarn build

View File

@@ -8,9 +8,6 @@ on:
workflow_dispatch: {}
pull_request:
branches: [master]
paths-ignore:
- 'docs/**'
- '**.md'
env:
# Common versions
@@ -71,6 +68,11 @@ jobs:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Get dependencies
run: |
go get -v -t -d ./...
@@ -106,10 +108,11 @@ jobs:
run: |
timeout 60 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:38081/api/workloads)" != "200" ]]; do sleep 5; done' || false
- name: Run api e2e tests
run: make e2e-api-test
- name: Run e2e tests
run: |
make e2e-api-test
make e2e-test
run: make e2e-test
lint:
runs-on: ubuntu-20.04
@@ -157,5 +160,8 @@ jobs:
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-pkg-
- name: Check code formatting
run: go install golang.org/x/tools/cmd/goimports && make fmt
- name: Check Diff
run: make check-diff

View File

@@ -26,11 +26,11 @@ jobs:
- name: Use Node.js 10.x
uses: actions/setup-node@v1
with:
node-version: 10.x
- name: Run npm install
run: make npm-install
- name: Run npm build
run: make npm-build
node-version: 12.x
- name: Run dashboard install
run: make dashboard-install
- name: Run dashboard build
run: make dashboard-build
- name: Tag helm chart image
run: |
sed -i 's/latest/${{ steps.get_version.outputs.VERSION }}/g' charts/vela-core/values.yaml

3
.gitignore vendored
View File

@@ -51,3 +51,6 @@ dashboard/package-lock.json
dashboard/src/.umi/
package-lock.json
dashboard/src/.umi-production/
# Swagger: generate Restful API
pkg/server/docs/index.html

View File

@@ -103,6 +103,10 @@ linters-settings:
rangeValCopy:
sizeThreshold: 32
makezero:
# Allow only slices initialized with a length of zero. Default is false.
always: false
linters:
enable:
- megacheck

View File

@@ -2,7 +2,8 @@
## About KubeVela
KubeVela project is initialized and maintained by the cloud native community since day 0 with [bootstrapping contributors from 8+ different organizations](https://github.com/oam-dev/kubevela/graphs/contributors). We intend for KubeVela to have a open governance since the very beginning and donate the project to neutral foundation as soon as it's released.
KubeVela project is initialized and maintained by the cloud native community since day 0 with [bootstrapping contributors from 8+ different organizations](https://github.com/oam-dev/kubevela/graphs/contributors).
We intend for KubeVela to have an open governance since the very beginning and donate the project to neutral foundation as soon as it's released.
This doc explains how to set up a development environment, so you can get started
contributing to `kubevela` or build a PoC (Proof of Concept).
@@ -27,15 +28,22 @@ We also recommend you to learn about KubeVela's [design](docs/en/design.md) befo
git clone git@github.com:oam-dev/kubevela.git
```
KubeVela includes two parts, `vela core` and `vela cli`.
- The `vela core` is actually a K8s controller, it will watch OAM Spec CRD and deploy resources.
- The `vela cli` is a command line tool that can build, run apps(with the help of `vela core`).
For local development, we probably need to build both of them.
* Build Vela CLI
```shell script
make
```
* Configure vela to PATH
After the vela cli built successfully, `make` command will create `vela` binary to `bin/` under the project.
after build, make will create `vela` binary to `bin/`, Set this path to PATH.
* Configure `vela` binary to System PATH
```shell script
export PATH=$PATH:/your/path/to/project/kubevela/bin
@@ -51,7 +59,7 @@ make manager
* Run Vela Core
Firstly make sure your cluster has CRDs.
Firstly make sure your cluster has CRDs, below is the command that can help install all CRDs.
```shell script
make core-install
@@ -66,74 +74,19 @@ make core-run
This command will run controller locally, it will use your local KubeConfig which means you need to have a k8s cluster
locally. If you don't have a one, we suggest that you could setup up a cluster with [kind](https://kind.sigs.k8s.io/).
When you're developing `vela-core`, make sure the controller installed by `vela install` is not running.
Otherwise, it will conflict with your local running controller.
You can check and uninstall it by using helm.
```shell script
helm list -A
helm uninstall -n vela-system kubevela
```
### Use
* Create environment
```shell script
vela env init myenv --namespace myenv --email my@email.com --domain kubevela.io
```
* Create Component
For example, use the following command to create and run an application.
```shell script
$ vela svc deploy mysvc -t webservice --image crccheck/hello-world --port 8000 -a abc
App abc deployed
```
* Add Trait
```shell script
$ vela route abc
Adding route for app mysvc
⠋ Deploying ...
✅ Application Deployed Successfully!
- Name: mysvc
Type: webservice
HEALTHY Ready: 1/1
Last Deployment:
Created at: 2020-11-02 11:17:28 +0800 CST
Updated at: 2020-11-02T11:21:23+08:00
Routes:
- route: Visiting URL: http://abc.kubevela.io IP: 47.242.68.137
```
* Check Status
```
$ vela status abc
About:
Name: abc
Namespace: default
Created at: 2020-11-02 11:17:28.067738 +0800 CST
Updated at: 2020-11-02 11:28:13.490986 +0800 CST
Services:
- Name: mysvc
Type: webservice
HEALTHY Ready: 1/1
Last Deployment:
Created at: 2020-11-02 11:17:28 +0800 CST
Updated at: 2020-11-02T11:28:13+08:00
Routes:
- route: Visiting URL: http://abc.kubevela.io IP: 47.242.68.137
```
* Delete App
```shell script
$ vela ls
SERVICE APP TYPE TRAITS STATUS CREATED-TIME
mysvc abc Deployed 2020-11-02 11:17:28 +0800 CST
$ vela delete abc
Deleting Application "abc"
delete apps succeed abc from default
```
You can try use your local built binaries follow [the documentation](https://kubevela.io/#/en/quick-start).
## Testing
@@ -158,7 +111,18 @@ make e2e-test
```
## Make a pull request
Remember to write unit-test and e2e test before making a pull request.
Remember to write unit-test and e2e-test after you have finished your code.
Run following checks before making a pull request.
```shell script
make reviewable
```
The command will do some lint checks and clean code.
After that, check in all changes and send a pull request.
## Merge Regulations

View File

@@ -49,19 +49,26 @@ build: fmt vet lint
@$(OK) build succeed
vela-cli:
go run hack/chart/generate.go
go build -o bin/vela -ldflags ${LDFLAGS} cmd/vela/main.go
git checkout cmd/vela/fake/chart_source.go
npm-build:
cd dashboard && npm run build && cd ./..
dashboard-build:
cd dashboard && yarn build && cd ./..
npm-install:
cd dashboard && npm install && cd ./..
dashboard-install:
cd dashboard && yarn && cd ./..
doc-gen:
rm -r docs/en/cli/*
go run hack/docgen/gen.go
go run hack/references/generate.go
api-gen:
swag init -g pkg/server/route.go --output pkg/server/docs
swagger-codegen generate -l html2 -i pkg/server/docs/swagger.yaml -o pkg/server/docs
mv pkg/server/docs/index.html docs/en/developers/references/restful-api/
generate-source:
go run hack/frontend/source.go
@@ -98,7 +105,7 @@ vet:
lint: golangci
$(GOLANGCILINT) run ./...
reviewable: fmt vet lint manifests
reviewable: manifests fmt vet lint
go mod tidy
# Execute auto-gen code commands and ensure branch is clean.
@@ -121,17 +128,19 @@ e2e-setup:
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=vela-core,app.kubernetes.io/instance=kubevela -n vela-system --timeout=600s
bin/vela dashboard &
e2e-api-test:
# Run e2e test
ginkgo -v -skipPackage capability,setup,apiserver,application -r e2e
ginkgo -v -r e2e/apiserver
ginkgo -v -r e2e/application
e2e-test:
# Run e2e test
ginkgo -v -skipPackage capability,setup,apiserver -r e2e
ginkgo -v ./test/e2e-test
# integration test will clean environment, please don't put test behind it.
CGO_ENABLED=0 go test -timeout 1h -count=1 -v -tags 'integration' ./test/integration
@$(OK) tests pass
e2e-api-test:
# Run e2e test
ginkgo -v -r e2e/apiserver
e2e-cleanup:
# Clean up
@@ -220,4 +229,11 @@ ifeq (, $(shell which cue))
CUE=$(GOBIN)/cue
else
CUE=$(shell which cue)
endif
endif
start-dashboard:
go run pkg/server/main/startAPIServer.go &
cd dashboard && yarn && yarn start && cd ..
swagger-gen:
$(GOBIN)/swag init -g server/route.go -d pkg/ -o pkg/server/docs/

8
OWNERS
View File

@@ -1,8 +1,12 @@
approvers:
- kubevela-controller
- kubevela-devex
- kubevela-dashboard-approver
reviewers:
- kubevela-controller
- kubevela-dashboard
- oam-spec
- oam-spec
- kubevela-dashboard-reviewer
members:
- community-collaborators

View File

@@ -3,11 +3,14 @@ aliases:
- hongchaodeng
- wonderflow
kubevela-dashboard:
kubevela-dashboard-approver:
- zzxwill
- hanxie-crypto
- hongchaodeng
kubevela-dashboard-reviewer:
- sunny0826
- hanxie-crypto
kubevela-controller:
- resouer
- wonderflow
@@ -19,7 +22,6 @@ aliases:
oam-spec: # inherit from https://github.com/oam-dev/spec/blob/master/OWNERS.md
- hongchaodeng
- resouer
- vturecek
community-collaborators:
- Fei-Guo

View File

@@ -13,18 +13,28 @@
# KubeVela
For developers, KubeVela is an easy-to-use tool that enables them to describe and ship their applications to Kubernetes with minimal effort.
For developers, KubeVela is an easy-to-use yet extensible platform that enables them to design and ship applications with minimal effort.
For platform builders, KubeVela serves as a framework that empowers them to create developer facing yet highly extensible platforms at ease.
For platform builders, KubeVela is the core engine that empowers them to create above platform with ease.
- Slack: [Discuss](https://cloud-native.slack.com/archives/C01BLQ3HTJA)
- Gitter: [Community](https://gitter.im/oam-dev/community)
## Community
- Slack: [CNCF Slack](https://slack.cncf.io/) #kubevela channel
- Gitter: [Discussion](https://gitter.im/oam-dev/community)
> NOTE: KubeVela is still in early stage and iterating quickly. It's currently under preview release.
## How It Works?
![alt](docs/resources/how-it-works.png)
## Quick Start
Quick start guides are available on [this section](https://kubevela.io/#/en/quick-start).
Quick start guides for developers are available on [this section](https://kubevela.io/#/en/quick-start).
## Platform Builder Guide
Detailed guides for platform teams are available on [this section](https://kubevela.io/#/en/platform-engineers/overview).
## Documentation

View File

@@ -44,6 +44,39 @@ type AppStatus struct {
runtimev1alpha1.ConditionedStatus `json:",inline"`
Phase ApplicationPhase `json:"status,omitempty"`
// Components record the related Components created by Application Controller
Components []runtimev1alpha1.TypedReference `json:"components,omitempty"`
}
// ApplicationTrait defines the trait of application
type ApplicationTrait struct {
Name string `json:"name"`
// +kubebuilder:pruning:PreserveUnknownFields
Properties runtime.RawExtension `json:"properties"`
}
// ApplicationComponent describe the component of application
type ApplicationComponent struct {
Name string `json:"name"`
WorkloadType string `json:"type"`
// +kubebuilder:pruning:PreserveUnknownFields
Settings runtime.RawExtension `json:"settings"`
// Traits define the trait of one component, the type must be array to keep the order.
Traits []ApplicationTrait `json:"traits,omitempty"`
// +kubebuilder:pruning:PreserveUnknownFields
// scopes in ApplicationComponent defines the component-level scopes
// the format is <scope-type:scope-instance-name> pairs, the key represents type of `ScopeDefinition` while the value represent the name of scope instance.
Scopes map[string]string `json:"scopes,omitempty"`
}
// ApplicationSpec is the spec of Application
type ApplicationSpec struct {
Components []ApplicationComponent `json:"components"`
// TODO(wonderflow): we should have application level scopes supported here
}
// +kubebuilder:object:root=true
@@ -54,9 +87,8 @@ type Application struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// +kubebuilder:pruning:PreserveUnknownFields
Spec runtime.RawExtension `json:"spec,omitempty"`
Status AppStatus `json:"status,omitempty"`
Spec ApplicationSpec `json:"spec,omitempty"`
Status AppStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
@@ -67,7 +99,3 @@ type ApplicationList struct {
metav1.ListMeta `json:"metadata,omitempty"`
Items []Application `json:"items"`
}
func init() {
SchemeBuilder.Register(&Application{}, &ApplicationList{})
}

View File

@@ -97,7 +97,7 @@ type WorkloadDefinitionList struct {
// A TraitDefinitionSpec defines the desired state of a TraitDefinition.
type TraitDefinitionSpec struct {
// Reference to the CustomResourceDefinition that defines this trait kind.
Reference DefinitionReference `json:"definitionRef"`
Reference DefinitionReference `json:"definitionRef,omitempty"`
// Revision indicates whether a trait is aware of component revision
// +optional
@@ -114,6 +114,18 @@ type TraitDefinitionSpec struct {
// +optional
AppliesToWorkloads []string `json:"appliesToWorkloads,omitempty"`
// ConflictsWith specifies the list of traits(CRD name, Definition name, CRD group)
// which could not apply to the same workloads with this trait.
// Traits that omit this field can work with any other traits.
// Example rules:
// "service" # Trait definition name
// "services.k8s.io" # API resource/crd name
// "*.networking.k8s.io" # API group
// "labelSelector:foo=bar" # label selector
// labelSelector format: https://pkg.go.dev/k8s.io/apimachinery/pkg/labels#Parse
// +optional
ConflictsWith []string `json:"conflictsWith,omitempty"`
// Extension is used for extension needs by OAM platform builders
// +optional
// +kubebuilder:pruning:PreserveUnknownFields
@@ -504,6 +516,12 @@ type DataInput struct {
// ToFieldPaths specifies the field paths of an object to fill passed value.
ToFieldPaths []string `json:"toFieldPaths,omitempty"`
// StrategyMergeKeys specifies the merge key if the toFieldPaths target is an array.
// The StrategyMergeKeys is optional, by default, if the toFieldPaths target is an array, we will append.
// If StrategyMergeKeys specified, we will check the key in the target array.
// If any key exist, do update; if no key exist, append.
StrategyMergeKeys []string `json:"strategyMergeKeys,omitempty"`
}
// DataInputValueFrom specifies the value source for a data input.

View File

@@ -101,6 +101,14 @@ var (
HealthScopeGroupVersionKind = SchemeGroupVersion.WithKind(HealthScopeKind)
)
// Application type metadata.
var (
ApplicationKind = reflect.TypeOf(Application{}).Name()
ApplicationGroupKind = schema.GroupKind{Group: Group, Kind: ApplicationKind}.String()
ApplicationKindAPIVersion = ApplicationKind + "." + SchemeGroupVersion.String()
ApplicationKindVersionKind = SchemeGroupVersion.WithKind(ApplicationKind)
)
func init() {
SchemeBuilder.Register(&WorkloadDefinition{}, &WorkloadDefinitionList{})
SchemeBuilder.Register(&TraitDefinition{}, &TraitDefinitionList{})
@@ -110,5 +118,6 @@ func init() {
SchemeBuilder.Register(&ContainerizedWorkload{}, &ContainerizedWorkloadList{})
SchemeBuilder.Register(&ManualScalerTrait{}, &ManualScalerTraitList{})
SchemeBuilder.Register(&HealthScope{}, &HealthScopeList{})
SchemeBuilder.Register(&Application{}, &ApplicationList{})
SchemeBuilder.Register(&ApplicationDeployment{}, &ApplicationDeploymentList{})
}

View File

@@ -29,6 +29,11 @@ import (
func (in *AppStatus) DeepCopyInto(out *AppStatus) {
*out = *in
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
if in.Components != nil {
in, out := &in.Components, &out.Components
*out = make([]v1alpha1.TypedReference, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppStatus.
@@ -68,6 +73,36 @@ func (in *Application) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationComponent) DeepCopyInto(out *ApplicationComponent) {
*out = *in
in.Settings.DeepCopyInto(&out.Settings)
if in.Traits != nil {
in, out := &in.Traits, &out.Traits
*out = make([]ApplicationTrait, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Scopes != nil {
in, out := &in.Scopes, &out.Scopes
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationComponent.
func (in *ApplicationComponent) DeepCopy() *ApplicationComponent {
if in == nil {
return nil
}
out := new(ApplicationComponent)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationConfiguration) DeepCopyInto(out *ApplicationConfiguration) {
*out = *in
@@ -346,6 +381,44 @@ func (in *ApplicationList) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationSpec) DeepCopyInto(out *ApplicationSpec) {
*out = *in
if in.Components != nil {
in, out := &in.Components, &out.Components
*out = make([]ApplicationComponent, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSpec.
func (in *ApplicationSpec) DeepCopy() *ApplicationSpec {
if in == nil {
return nil
}
out := new(ApplicationSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ApplicationTrait) DeepCopyInto(out *ApplicationTrait) {
*out = *in
in.Properties.DeepCopyInto(&out.Properties)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationTrait.
func (in *ApplicationTrait) DeepCopy() *ApplicationTrait {
if in == nil {
return nil
}
out := new(ApplicationTrait)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CPUResources) DeepCopyInto(out *CPUResources) {
*out = *in
@@ -941,6 +1014,11 @@ func (in *DataInput) DeepCopyInto(out *DataInput) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.StrategyMergeKeys != nil {
in, out := &in.StrategyMergeKeys, &out.StrategyMergeKeys
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataInput.
@@ -1616,6 +1694,11 @@ func (in *TraitDefinitionSpec) DeepCopyInto(out *TraitDefinitionSpec) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ConflictsWith != nil {
in, out := &in.ConflictsWith, &out.ConflictsWith
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Extension != nil {
in, out := &in.Extension, &out.Extension
*out = new(runtime.RawExtension)

View File

@@ -50,6 +50,11 @@ spec:
items:
description: DataInput specifies a data input sink to an object. If input is array, it will be appended to the target field paths.
properties:
strategyMergeKeys:
description: StrategyMergeKeys specifies the merge key if the toFieldPaths target is an array. The StrategyMergeKeys is optional, by default, if the toFieldPaths target is an array, we will append. If StrategyMergeKeys specified, we will check the key in the target array. If any key exist, do update; if no key exist, append.
items:
type: string
type: array
toFieldPaths:
description: ToFieldPaths specifies the field paths of an object to fill passed value.
items:
@@ -166,6 +171,11 @@ spec:
items:
description: DataInput specifies a data input sink to an object. If input is array, it will be appended to the target field paths.
properties:
strategyMergeKeys:
description: StrategyMergeKeys specifies the merge key if the toFieldPaths target is an array. The StrategyMergeKeys is optional, by default, if the toFieldPaths target is an array, we will append. If StrategyMergeKeys specified, we will check the key in the target array. If any key exist, do update; if no key exist, append.
items:
type: string
type: array
toFieldPaths:
description: ToFieldPaths specifies the field paths of an object to fill passed value.
items:

View File

@@ -30,11 +30,75 @@ spec:
metadata:
type: object
spec:
description: ApplicationSpec is the spec of Application
properties:
components:
items:
description: ApplicationComponent describe the component of application
properties:
name:
type: string
scopes:
additionalProperties:
type: string
description: scopes in ApplicationComponent defines the component-level scopes the format is <scope-type:scope-instance-name> pairs, the key represents type of `ScopeDefinition` while the value represent the name of scope instance.
type: object
x-kubernetes-preserve-unknown-fields: true
settings:
type: object
x-kubernetes-preserve-unknown-fields: true
traits:
description: Traits define the trait of one component, the type must be array to keep the order.
items:
description: ApplicationTrait defines the trait of application
properties:
name:
type: string
properties:
type: object
x-kubernetes-preserve-unknown-fields: true
required:
- name
- properties
type: object
type: array
type:
type: string
required:
- name
- settings
- type
type: object
type: array
required:
- components
type: object
x-kubernetes-preserve-unknown-fields: true
status:
description: AppStatus defines the observed state of Application
properties:
components:
description: Components record the related Components created by Application Controller
items:
description: A TypedReference refers to an object by Name, Kind, and APIVersion. It is commonly used to reference cluster-scoped objects or objects where the namespace is already known.
properties:
apiVersion:
description: APIVersion of the referenced object.
type: string
kind:
description: Kind of the referenced object.
type: string
name:
description: Name of the referenced object.
type: string
uid:
description: UID of the referenced object.
type: string
required:
- apiVersion
- kind
- name
type: object
type: array
conditions:
description: Conditions of the resource.
items:

View File

@@ -44,6 +44,11 @@ spec:
items:
type: string
type: array
conflictsWith:
description: 'ConflictsWith specifies the list of traits(CRD name, Definition name, CRD group) which could not apply to the same workloads with this trait. Traits that omit this field can work with any other traits. Example rules: "service" # Trait definition name "services.k8s.io" # API resource/crd name "*.networking.k8s.io" # API group "labelSelector:foo=bar" # label selector labelSelector format: https://pkg.go.dev/k8s.io/apimachinery/pkg/labels#Parse'
items:
type: string
type: array
definitionRef:
description: Reference to the CustomResourceDefinition that defines this trait kind.
properties:
@@ -66,8 +71,6 @@ spec:
workloadRefPath:
description: WorkloadRefPath indicates where/if a trait accepts a workloadRef object
type: string
required:
- definitionRef
type: object
type: object
served: true

View File

@@ -7,4 +7,4 @@ spec:
workloadRefsPath: spec.workloadRefs
allowComponentOverlap: true
definitionRef:
name: healthscope.core.oam.dev
name: healthscopes.core.oam.dev

View File

@@ -4,7 +4,7 @@ kind: TraitDefinition
metadata:
name: autoscale
annotations:
definition.oam.dev/description: "`Autoscale` is used to automatically scale workloads by resource utilization metrics or cron triggers."
definition.oam.dev/description: "Automatically scales workloads by resource utilization metrics or cron triggers."
spec:
appliesToWorkloads:
- webservice

View File

@@ -3,7 +3,7 @@ apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: "`Scaler` is used to configure replicas for your service."
definition.oam.dev/description: "Configures replicas for your service."
name: scaler
spec:
appliesToWorkloads:

View File

@@ -4,7 +4,7 @@ kind: TraitDefinition
metadata:
name: metrics
annotations:
definition.oam.dev/description: "`Metrics` is used to configure monitoring metrics for your service."
definition.oam.dev/description: "Configures monitoring metrics for your service."
spec:
appliesToWorkloads:
- webservice

View File

@@ -4,7 +4,7 @@ kind: TraitDefinition
metadata:
name: rollout
annotations:
definition.oam.dev/description: "`Rollout` is used to configure Canary deployment strategy for your application."
definition.oam.dev/description: "Configures Canary deployment strategy for your application."
spec:
appliesToWorkloads:
- webservice

View File

@@ -4,7 +4,7 @@ kind: TraitDefinition
metadata:
name: route
annotations:
definition.oam.dev/description: "`Route` is used to configure external access to your service."
definition.oam.dev/description: "Configures external access to your service."
spec:
appliesToWorkloads:
- webservice
@@ -28,6 +28,8 @@ spec:
if parameter["rules"] != _|_ {
rules: parameter.rules
}
provider: *"nginx" | parameter.provider
}
}
parameter: {
@@ -39,5 +41,6 @@ spec:
path: string
rewriteTarget: *"" | string
}]
provider?: string
}

View File

@@ -4,7 +4,7 @@ kind: WorkloadDefinition
metadata:
name: task
annotations:
definition.oam.dev/description: "`Task` is a workload type to describe jobs that run code or a script to completion."
definition.oam.dev/description: "Describes jobs that run code or a script to completion."
spec:
definitionRef:
name: jobs.batch

View File

@@ -4,8 +4,8 @@ kind: WorkloadDefinition
metadata:
name: webservice
annotations:
definition.oam.dev/description: "`Webservice` is a workload type to describe long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers.
If workload type is skipped for any service defined in Appfile, it will be defaulted to `Web Service` type."
definition.oam.dev/description: "Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers.
If workload type is skipped for any service defined in Appfile, it will be defaulted to `webservice` type."
spec:
definitionRef:
name: deployments.apps

View File

@@ -4,7 +4,7 @@ kind: WorkloadDefinition
metadata:
name: worker
annotations:
definition.oam.dev/description: "`Worker` is a workload type to describe long-running, scalable, containerized services that running at backend. They do NOT have network endpoint to receive external network traffic."
definition.oam.dev/description: "Describes long-running, scalable, containerized services that running at backend. They do NOT have network endpoint to receive external network traffic."
spec:
definitionRef:
name: deployments.apps

View File

@@ -3,11 +3,55 @@
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
name: mutating-webhook-configuration
name: kubevela-mutating-webhook-configuration
namespace: {{ .Release.Namespace }}
annotations:
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ .Values.certificate.certificateName }}
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: {{ template "kubevela.name" . }}-webhook
namespace: {{ .Release.Namespace }}
path: /mutating-core-oam-dev-v1alpha2-applicationconfigurations
failurePolicy: Fail
name: mutating.core.oam.dev.v1alpha2.applicationconfigurations
rules:
- apiGroups:
- core.oam.dev
apiVersions:
- v1alpha2
operations:
- CREATE
- UPDATE
resources:
- applicationconfigurations
scope: Namespaced
admissionReviewVersions:
- v1beta1
timeoutSeconds: 5
- clientConfig:
caBundle: Cg==
service:
name: {{ template "kubevela.name" . }}-webhook
namespace: {{ .Release.Namespace }}
path: /mutating-core-oam-dev-v1alpha2-components
failurePolicy: Fail
name: mutating.core.oam-dev.v1alpha2.components
rules:
- apiGroups:
- core.oam.dev
apiVersions:
- v1alpha2
operations:
- CREATE
- UPDATE
resources:
- components
scope: Namespaced
admissionReviewVersions:
- v1beta1
timeoutSeconds: 5
- clientConfig:
caBundle: Cg==
service:
@@ -49,11 +93,55 @@ webhooks:
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
name: validating-webhook-configuration
name: kubevela-validating-webhook-configuration
namespace: {{ .Release.Namespace }}
annotations:
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ .Values.certificate.certificateName }}
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: {{ template "kubevela.name" . }}-webhook
namespace: {{ .Release.Namespace }}
path: /validating-core-oam-dev-v1alpha2-applicationconfigurations
failurePolicy: Fail
name: validating.core.oam.dev.v1alpha2.applicationconfigurations
rules:
- apiGroups:
- core.oam.dev
apiVersions:
- v1alpha2
operations:
- CREATE
- UPDATE
resources:
- applicationconfigurations
scope: Namespaced
admissionReviewVersions:
- v1beta1
timeoutSeconds: 5
- clientConfig:
caBundle: Cg==
service:
name: {{ template "kubevela.name" . }}-webhook
namespace: {{ .Release.Namespace }}
path: /validating-core-oam-dev-v1alpha2-components
failurePolicy: Fail
name: validating.core.oam.dev.v1alpha2.components
rules:
- apiGroups:
- core.oam.dev
apiVersions:
- v1alpha2
operations:
- CREATE
- UPDATE
resources:
- components
scope: Namespaced
admissionReviewVersions:
- v1beta1
timeoutSeconds: 5
- clientConfig:
caBundle: Cg==
service:

View File

@@ -12,8 +12,6 @@ import (
"syscall"
"time"
"github.com/oam-dev/kubevela/version"
monitoring "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
"github.com/crossplane/crossplane-runtime/pkg/logging"
"github.com/go-logr/logr"
@@ -42,6 +40,7 @@ import (
"github.com/oam-dev/kubevela/pkg/controller/utils"
velawebhook "github.com/oam-dev/kubevela/pkg/webhook"
oamwebhook "github.com/oam-dev/kubevela/pkg/webhook/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/version"
)
const (
@@ -95,6 +94,8 @@ func main() {
flag.StringVar(&healthAddr, "health-addr", ":9440", "The address the health endpoint binds to.")
flag.BoolVar(&controllerArgs.ApplyOnceOnly, "apply-once-only", false,
"For the purpose of some production environment that workload or trait should not be affected if no spec change")
flag.StringVar(&controllerArgs.CustomRevisionHookURL, "custom-revision-hook-url", "",
"custom-revision-hook-url is a webhook url which will let KubeVela core to call with applicationConfiguration and component info and return a customized component revision")
flag.StringVar(&disableCaps, "disable-caps", "", "To be disabled builtin capability list.")
flag.Parse()

15
community.md Normal file
View File

@@ -0,0 +1,15 @@
# Community
All contributors should be welcomed to the community by existing members, helped with PR workflow, and directed to relevant documentation and communication channels.
This doc outlines the various responsibilities of contributor roles in
KubeVela.
| Role | Responsibilities | Requirements | Defined by |
| -----| ---------------- | ------------ | -------|
| approver | Approve and merge PRs | Highly experienced and active member to a subcomponent. Sponsored by majority of approvers. | [OWNERS] file approver entry|
| reviewer | Review contributions | Active contributor and/or code reviewer. Contributed and own major features. Sponsored by 2 approvers. | [OWNERS] file reviewer entry. |
| member | Active contributor in the community. | Active contributor and/or code reviewer. Submitted 5 coding PRs or 10 doc PRs. Sponsored by 2 reviewers/approvers. | OAM GitHub org member. |
Established community members are expected to continuously demonstrate their commitment and contribution to the KubeVela projects.

View File

@@ -1,5 +1,4 @@
# Vela Server
## example
# Application Example
In this Demo, Application application-sample will be converted to appconfig and component
@@ -28,15 +27,26 @@ metadata:
name: application-sample
namespace: oam-test
spec:
services:
myweb:
cmd:
- sleep
- "1000"
image: busybox
scaler:
replicas: 10
components:
- name: myweb
type: worker
settings:
image: "busybox"
cmd:
- sleep
- "1000"
traits:
- name: scaler
properties:
replicas: 10
- name: sidercar
properties:
name: "sidecar-test"
image: "nginx"
- name: kservice
properties:
http:
server: 80
status:
conditions:
- lastTransitionTime: "2020-12-02T12:12:52Z"
@@ -74,34 +84,21 @@ metadata:
kind: Application
name: application-sample
uid: dca7acc3-664c-422b-aa52-4fe012e37974
spec:
spec:
components:
- componentName: myweb
traits:
- trait:
apiVersion: core.oam.dev/v1alpha2
kind: ManualScalerTrait
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
replicaCount: 10
status:
conditions:
- lastTransitionTime: "2020-12-02T12:12:52Z"
reason: Successfully reconciled resource
status: "True"
type: Synced
dependency: {}
workloads:
- componentName: myweb
componentRevisionName: myweb-v1
traits:
- traitRef:
apiVersion: core.oam.dev/v1alpha2
kind: ManualScalerTrait
name: myweb-trait-78fdd467d6
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: myweb
ports:
- port: 80
targetPort: 80
selector:
app: myweb
kubectl get component/myweb -oyaml
@@ -124,12 +121,14 @@ spec:
apiVersion: apps/v1
kind: Deployment
spec:
replicas: 10
selector:
matchLabels:
app.oam.dev/component: myweb
template:
metadata:
labels:
app: myweb
app.oam.dev/component: myweb
spec:
containers:
@@ -138,9 +137,10 @@ spec:
- "1000"
image: busybox
name: myweb
status:
latestRevision:
name: myweb-v1
revision: 1
- command:
- sleep
- "1000"
image: busybox
name: test-sidecar
```

View File

@@ -0,0 +1,388 @@
# Definition Docs
## Reserved word
### Patch
Perform the CUE AND operation with the content declared by 'patch' and workload cr,
you can define the strategy of list merge through comments, example as follows
base model
```
containers: [{
name: "x1"
}, {
name: "x2"
image: string
envs: [{
name: "OPS"
value: string
}, ...]
}, ...]
```
define patch model
```
// +patchKey=name
containers: [{
name: "x2"
image: "test-image"
envs: [{
name: "OPS1"
value: "V-OPS1"
},{
name: "OPS"
value: "V-OPS"
}, ...]
}, ...]
```
and the result model after patch is follow
```
containers: [{
name: "x1"
},{
name: "x2"
image: "test-image"
envs: [{
name: "OPS1"
value: "V-OPS1"
},{
name: "OPS"
value: "V-OPS"
}, ...]
}, ...]
```
### output
Generate a new cr, which is generally associated with workload cr
## Workload Definition
The following workload definition is to generate a deployment
```
apiVersion: core.oam.dev/v1alpha2
kind: WorkloadDefinition
metadata:
name: worker
annotations:
definition.oam.dev/description: "Long-running scalable backend worker without network endpoint"
spec:
definitionRef:
name: deployments.apps
extension:
template: |
output: {
apiVersion: "apps/v1"
kind: "Deployment"
spec: {
selector: matchLabels: {
"app.oam.dev/component": context.name
}
template: {
metadata: labels: {
"app.oam.dev/component": context.name
}
spec: {
containers: [{
name: context.name
image: parameter.image
if parameter["cmd"] != _|_ {
command: parameter.cmd
}
}]
}
}
}
}
parameter: {
// +usage=Which image would you like to use for your service
// +short=i
image: string
cmd?: [...string]
}
```
If defined an application as follows
```
apiVersion: core.oam.dev/v1alpha2
kind: Application
metadata:
name: application-sample
spec:
components:
- name: myweb
type: worker
settings:
image: "busybox"
cmd:
- sleep
- "1000"
```
we will get a deployment
```
apiVersion: apps/v1
kind: Deployment
spec:
selector:
matchLabels:
app.oam.dev/component: myweb
template:
metadata:
labels:
app.oam.dev/component: myweb
spec:
containers:
- command:
- sleep
- "1000"
image: busybox
name: myweb
```
## Service Trait Definition
Define a trait Definition that appends service to workload(worker) , as shown below
```
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: "service the app"
name: kservice
spec:
appliesToWorkloads:
- webservice
- worker
definitionRef:
name: service.v1
extension:
template: |-
patch: {spec: template: metadata: labels: app: context.name}
output: {
apiVersion: "v1"
kind: "Service"
metadata: name: context.name
spec: {
selector: app: context.name
ports: [
for k, v in parameter.http {
port: v
targetPort: v
}
]
}
}
parameter: {
http: [string]: int
}
```
If add service capability to the application, as follows
```
apiVersion: core.oam.dev/v1alpha2
kind: Application
metadata:
name: application-sample
spec:
components:
- name: myweb
type: worker
settings:
image: "busybox"
cmd:
- sleep
- "1000"
traits:
- name: kservice
properties:
http:
server: 80
```
we will get a new deployment and service
```
// origin deployment template add labels
apiVersion: apps/v1
kind: Deployment
spec:
selector:
matchLabels:
app.oam.dev/component: myweb
template:
metadata:
labels:
// add label app
app: myweb
app.oam.dev/component: myweb
spec:
containers:
- command:
- sleep
- "1000"
image: busybox
name: myweb
---
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
ports:
- port: 80
targetPort: 80
selector:
app: myweb
```
## Scaler Trait Definition
Define a trait Definition that scale workload(worker) replicas
```
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: "Manually scale the app"
name: scaler
spec:
appliesToWorkloads:
- webservice
- worke
extension:
template: |-
patch: {
spec: replicas: parameter.replicas
}
parameter: {
//+short=r
replicas: *1 | int
}
```
If add scaler capability to the application, as follows
```
components:
- name: myweb
type: worker
settings:
image: "busybox"
cmd:
- sleep
- "1000"
traits:
- name: kservice
properties:
http:
server: 80
- name: scaler
properties:
replicas: 10
```
The deployment replicas will be scale to 10
```
apiVersion: apps/v1
kind: Deployment
spec:
selector:
matchLabels:
app.oam.dev/component: myweb
// scale to 10
replicas: 10
template:
metadata:
labels:
// add label app
app: myweb
app.oam.dev/component: myweb
spec:
containers:
- command:
- sleep
- "1000"
image: busybox
name: myweb
```
## Sidecar Trait Definition
Define a trait Definition that append containers to workload(worker)
```
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: "add sidecar to the app"
name: sidecar
spec:
appliesToWorkloads:
- webservice
- worke
extension:
template: |-
patch: {
// +patchKey=name
spec: template: spec: containers: [parameter]
}
parameter: {
name: string
image: string
command?: [...string]
}
```
If add sidercar capability to the application, as follows
```
apiVersion: core.oam.dev/v1alpha2
kind: Application
metadata:
name: application-sample
spec:
components:
- name: myweb
type: worker
settings:
image: "busybox"
cmd:
- sleep
- "1000"
traits:
- name: scaler
properties:
replicas: 10
- name: sidercar
properties:
name: "sidecar-test"
image: "nginx"
- name: kservice
properties:
http:
server: 80
```
The deployment updated as follows
```
apiVersion: apps/v1
kind: Deployment
spec:
selector:
matchLabels:
app.oam.dev/component: myweb
// scale to 10
replicas: 10
template:
metadata:
labels:
// add label app
app: myweb
app.oam.dev/component: myweb
spec:
containers:
- command:
- sleep
- "1000"
image: busybox
name: myweb
- name: sidecar-test
image: nginx
```

View File

@@ -0,0 +1,25 @@
apiVersion: core.oam.dev/v1alpha2
kind: Application
metadata:
name: application-sample
spec:
components:
- name: myweb
type: worker
settings:
image: "busybox"
cmd:
- sleep
- "1000"
traits:
- name: scaler
properties:
replicas: 10
- name: sidecar
properties:
name: "sidecar-test"
image: "nginx"
- name: kservice
properties:
http:
server: 80

View File

@@ -58,19 +58,76 @@ spec:
appliesToWorkloads:
- webservice
- worker
definitionRef:
name: manualscalertraits.core.oam.dev
workloadRefPath: spec.workloadRef
extension:
template: |-
output: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ManualScalerTrait"
spec: {
replicaCount: parameter.replicas
}
patch: {
spec: replicas: parameter.replicas
}
parameter: {
//+short=r
replicas: *1 | int
}
---
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: "add sidecar to the app"
name: sidecar
spec:
appliesToWorkloads:
- webservice
- worker
extension:
template: |-
patch: {
// +patchKey=name
spec: template: spec: containers: [parameter]
}
parameter: {
name: string
image: string
command?: [...string]
}
---
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: "service the app"
name: kservice
spec:
appliesToWorkloads:
- webservice
- worker
definitionRef:
name: services
extension:
template: |-
patch: {spec: template: metadata: labels: app: context.name}
output: {
apiVersion: "v1"
kind: "Service"
metadata: name: context.name
spec: {
selector: app: context.name
ports: [
for k, v in parameter.http {
port: v
targetPort: v
}
]
}
}
parameter: {
http: [string]: int
}
---
apiVersion: core.oam.dev/v1alpha2
kind: TraitDefinition
metadata:
name: services
namespace: default
spec:
definitionRef:
name: services

View File

@@ -1,14 +0,0 @@
apiVersion: core.oam.dev/v1alpha2
kind: Application
metadata:
name: application-sample
spec:
services:
myweb:
type: worker
image: "busybox"
cmd:
- sleep
- "1000"
scaler:
replicas: 10

View File

@@ -1,4 +1,8 @@
/lambda/
/scripts
/config
.history
.history
public
dist
.umi
mock

View File

@@ -5,4 +5,8 @@ module.exports = {
page: true,
REACT_APP_ENV: true,
},
rules: {
'react/jsx-uses-react': 'off',
'react/react-in-jsx-scope': 'off',
},
};

37
dashboard/.gitignore vendored Normal file
View File

@@ -0,0 +1,37 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
**/node_modules
# roadhog-api-doc ignore
/src/utils/request-temp.js
_roadhog-api-doc
# production
/dist
# misc
.DS_Store
npm-debug.log*
yarn-error.log
/coverage
.idea
*bak
.vscode
# visual studio code
.history
*.log
functions/*
.temp/**
# umi
.umi
.umi-production
# screenshot
screenshot
.firebase
.eslintcache
build

View File

@@ -20,3 +20,4 @@ yarn-error.log
.history
CNAME
/build
/public

View File

@@ -1,50 +1,81 @@
# Vela Dashboard
# KubeVela Dashboard
## Quick start
In the root folder of this project, run `make start-dashboard` to start backend OpenAPI server and Dashboard at the same time.
```shell
➜ xxx/src/github.com/oam-dev/kubevela $ make start-dashboard
go run pkg/server/main/startAPIServer.go &
cd dashboard && yarn && yarn start && cd ..
yarn install v1.22.4
warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json.
[1/5] 🔍 Validating package.json...
[2/5] 🔍 Resolving packages...
success Already up-to-date.
$ umi g tmp
✨ Done in 5.89s.
yarn run v1.22.4
$ umi dev
Starting the development server...
I1230 10:37:54.157092 14236 request.go:621] Throttling request took 1.04915427s, request: GET:https://47.242.145.141:6443/apis/split.smi-spec.io/v1alpha2?timeout=32s
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] POST /api/envs/ --> github.com/oam-dev/kubevela/pkg/server.(*APIServer).CreateEnv-fm (6 handlers)
[GIN-debug] PUT /api/envs/:envName --> github.com/oam-dev/kubevela/pkg/server.(*APIServer).UpdateEnv-fm (6 handlers)
...
[GIN-debug] GET /api/version --> github.com/oam-dev/kubevela/pkg/server.(*APIServer).GetVersion-fm (6 handlers)
[GIN-debug] GET /swagger/*any --> github.com/swaggo/gin-swagger.CustomWrapHandler.func1 (7 handlers)
✔ Webpack
Compiled successfully in 26.86s
DONE Compiled successfully in 26865ms 10:38:22 AM
## Environment Prepare
Install `node_modules`:
```bash
npm install
App running at:
- Local: http://localhost:8000 (copied to clipboard)
- Network: http://192.168.31.114:8000
```
or
## Development
### Install dependencies
```bash
yarn
```
## Provided Scripts
Scripts provided in `package.json`. It's safe to modify or add additional script:
### Start project
### Build
```bash
npm start
yarn build
```
### Build project
### Start up
```bash
npm run build
yarn start
```
### Check code style
### Lint and Test
- Check code style
```bash
npm run lint
yarn lint
```
You can also use script to auto fix some lint error:
```bash
npm run lint:fix
yarn prettier
```
### Test code
- Test code
```bash
npm test
```
yarn test
```

View File

@@ -1,153 +0,0 @@
// https://umijs.org/config/
import { defineConfig } from 'umi';
import defaultSettings from './defaultSettings';
import proxy from './proxy';
const { REACT_APP_ENV } = process.env;
export default defineConfig({
history: { type: 'hash' },
hash: false,
antd: {},
dva: {
hmr: true,
},
locale: {
default: 'en-US',
antd: false,
baseNavigator: false,
},
dynamicImport: {
loading: '@/components/PageLoading/index',
},
targets: {
ie: 11,
},
// umi routes: https://umijs.org/docs/routing
routes: [
{
path: '/',
component: '../layouts/SecurityLayout',
routes: [
{
path: '/',
component: '../layouts/BasicLayout',
routes: [
{
path: '/',
redirect: `/ApplicationList`,
},
{
name: 'ApplicationList',
icon: 'table',
path: `/ApplicationList`,
component: './ApplicationList',
},
{
name: 'ApplicationList.WorkloadDetail',
icon: 'smile',
path: '/ApplicationList/WorkloadDetail',
component: './Workload/Detail',
hideInMenu: true,
},
{
name: 'ApplicationList.TraitDetail',
icon: 'smile',
path: '/ApplicationList/TraitDetail',
component: './Traits/Detail',
hideInMenu: true,
},
{
name: 'ApplicationList.Components',
hideInMenu: true,
path: '/ApplicationList/:appName/Components',
component: './ApplicationList/Components',
},
{
name: 'ApplicationList.Components.createComponent',
hideInMenu: true,
path: '/ApplicationList/:appName/createComponent',
component: './ApplicationList/CreateComponent',
},
{
name: 'Workload',
icon: 'table',
path: '/Workload',
routes: [
{
name: 'WorkloadItem',
icon: 'smile',
path: '/Workload/:WorkloadType',
component: './Workload/index.jsx',
},
],
},
{
path: '/Traits',
name: 'Traits',
icon: 'table',
routes: [
{
name: 'TraitItem',
icon: 'smile',
path: '/Traits/:traitType',
component: './Traits/index.jsx',
},
],
},
{
name: 'Capability',
icon: 'table',
path: '/Capability',
component: './Capability',
},
{
path: '/System',
name: 'System',
icon: 'table',
routes: [
{
name: 'Env',
icon: 'table',
path: '/System/Env',
component: './System/Env',
},
],
},
{
name: 'Capability.Detail',
hideInMenu: true,
path: '/Capability/Detail',
component: './Capability/Detail',
},
{
component: './404',
},
],
},
{
component: './404',
},
],
},
{
component: './404',
},
],
// Theme for antd: https://ant.design/docs/react/customize-theme-cn
theme: {
// 主题配置
'primary-color': defaultSettings.primaryColor,
'link-color': defaultSettings.linkColor,
'link-hover-color': defaultSettings.linkHoverColor,
'disabled-bg': defaultSettings.disabledBg,
'disabled-color': defaultSettings.disabledColor,
'btn-disable-color': defaultSettings.btnDisableColor,
},
// @ts-ignore
title: false,
ignoreMomentLocale: true,
proxy: proxy[REACT_APP_ENV || 'dev'],
manifest: {
basePath: '/',
},
});

View File

@@ -0,0 +1,68 @@
// https://umijs.org/config/
import { defineConfig } from 'umi';
import defaultSettings from './defaultSettings';
import proxy from './proxy';
import routes from './routes';
import themeSettings from './themeSettings';
const { REACT_APP_ENV } = process.env;
export default defineConfig({
hash: true,
antd: {},
dva: {
hmr: true,
},
layout: {
name: 'KubeVela',
locale: true,
siderWidth: 208,
...defaultSettings,
},
locale: {
// default en-US
default: 'en-US',
antd: true,
// default true, when it is true, will use `navigator.language` overwrite default
baseNavigator: true,
},
dynamicImport: {
loading: '@ant-design/pro-layout/es/PageLoading',
},
nodeModulesTransform: {
type: 'none',
},
targets: {
ie: 11,
},
// umi routes: https://umijs.org/docs/routing
routes,
// Theme for antd: https://ant.design/docs/react/customize-theme-cn
theme: {
'primary-color': defaultSettings.primaryColor,
'link-color': themeSettings.linkColor,
'link-hover-color': themeSettings.linkHoverColor,
'disabled-bg': themeSettings.disabledBg,
'disabled-color': themeSettings.disabledColor,
'btn-disable-color': themeSettings.btnDisableColor,
},
esbuild: {},
title: false,
ignoreMomentLocale: true,
proxy: proxy[REACT_APP_ENV || 'dev'],
manifest: {
basePath: '/',
},
// https://github.com/zthxxx/react-dev-inspector
plugins: ['react-dev-inspector/plugins/umi/react-inspector'],
inspectorConfig: {
// loader options type and docs see below
exclude: [],
babelPlugins: [],
babelOptions: {},
},
resolve: {
includes: ['src/components'],
},
});

View File

@@ -1,22 +0,0 @@
const proSettings = {
navTheme: 'dark',
// 主题颜色配置
primaryColor: '#1B58F4', // 全局主色
linkColor: '#1B58F4', // 链接色
linkHoverColor: '#1B58F4',
disabledBg: '#EBEBEB', // 失效背景色,
disabledColor: '#BEBEBE', // 失效文本色,
btnDisableColor: '#A4A4A4', // 禁用btn文字颜色
layout: 'side',
contentWidth: 'Fluid',
fixedHeader: false,
fixSiderbar: true,
colorWeak: false,
menu: {
locale: false,
},
title: 'Micro App Engine',
pwa: false,
iconfontUrl: '',
};
export default proSettings;

View File

@@ -0,0 +1,22 @@
import { Settings as LayoutSettings } from '@ant-design/pro-layout';
import themeSettings from './themeSettings';
const Settings: LayoutSettings & {
pwa?: boolean;
logo?: string;
} = {
navTheme: 'dark',
primaryColor: themeSettings.primaryColor, // 全局主色
layout: 'mix',
contentWidth: 'Fluid',
fixedHeader: false,
fixSiderbar: true,
colorWeak: false,
title: 'KubeVela',
pwa: false,
logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
iconfontUrl: '',
};
export default Settings;

View File

@@ -1,33 +0,0 @@
/**
* 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
* The agent cannot take effect in the production environment
* so there is no configuration of the production environment
* For details, please see
* https://pro.ant.design/docs/deploy
*/
export default {
dev: {
'/api': {
target: 'http://127.0.0.1:38081/',
changeOrigin: true,
},
},
test: {
'/api/': {
target: 'https://preview.pro.ant.design',
changeOrigin: true,
pathRewrite: {
'^': '',
},
},
},
pre: {
'/api/': {
target: 'your pre url',
changeOrigin: true,
pathRewrite: {
'^': '',
},
},
},
};

20
dashboard/config/proxy.ts Normal file
View File

@@ -0,0 +1,20 @@
export default {
dev: {
'/api/': {
target: 'http://localhost:38081/',
changeOrigin: true,
},
},
test: {
'/api/': {
target: 'http://localhost:38081/',
changeOrigin: true,
},
},
pre: {
'/api/': {
target: 'http://localhost:38081/',
changeOrigin: true,
},
},
};

View File

@@ -0,0 +1,52 @@
export default [
{
path: '/',
redirect: `/System`,
},
{
name: 'applications',
icon: 'appstore',
path: `/applications`,
component: './Application',
},
{
name: 'capability',
icon: 'AppstoreAddOutlined',
path: '/capabilities',
routes: [
{
path: '/capabilities',
redirect: `/Capability/Workloads`,
},
{
name: 'workloads',
path: '/capabilities/workloads',
component: './Capability/Workloads'
},
{
name: 'traits',
path: '/capabilities/traits',
component: './Capability/Traits',
},
],
},
{
name: 'system',
icon: 'setting',
path: '/System',
routes: [
{
path: '/System',
redirect: `/System/Environment`,
},
{
name: 'environment',
path: '/System/Environment',
component: './System/Environment',
},
],
},
{
component: './404',
},
];

View File

@@ -0,0 +1,9 @@
const themeSettings = {
primaryColor: '#1b58f4', // 全局主色
linkColor: '#1b58f4', // 链接色
linkHoverColor: '#1b58f4',
disabledBg: '#ebebeb', // 失效背景色,
disabledColor: '#bebebe', // 失效文本色,
btnDisableColor: '#a4a4a4', // 禁用btn文字颜色
};
export default themeSettings;

View File

@@ -2,6 +2,7 @@ module.exports = {
testURL: 'http://localhost:8000',
testEnvironment: './tests/PuppeteerEnvironment',
verbose: false,
extraSetupFiles: ['./tests/setupTests.js'],
globals: {
ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: false,
localStorage: null,

View File

@@ -1,6 +1,6 @@
{
"name": "micro-app-engine",
"version": "4.2.0",
"name": "kubevela",
"version": "0.0.1",
"private": true,
"description": "An out-of-box UI solution for enterprise applications",
"scripts": {
@@ -8,19 +8,19 @@
"build": "umi build",
"deploy": "npm run site && npm run gh-pages",
"dev": "npm run start:dev",
"fetch:blocks": "pro fetch-blocks && npm run prettier",
"gh-pages": "gh-pages -d dist",
"i18n-remove": "pro i18n-remove --locale=zh-CN --write",
"postinstall": "umi g tmp",
"lint": "umi g tmp && npm run lint:js && npm run lint:style && npm run lint:prettier",
"lint-staged": "lint-staged",
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
"lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src && npm run lint:style",
"lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
"lint:prettier": "prettier --check \"**/*\" --end-of-line auto",
"lint:prettier": "prettier --check \"src/**/*\" --end-of-line auto",
"lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
"precommit": "lint-staged",
"prettier": "prettier -c --write \"**/*\"",
"start": "cross-env UMI_UI=none umi dev",
"prettier": "prettier -c --write \"src/**/*\"",
"start": "umi dev",
"start:dev": "cross-env REACT_APP_ENV=dev MOCK=none umi dev",
"start:no-mock": "cross-env MOCK=none umi dev",
"start:no-ui": "cross-env UMI_UI=none umi dev",
@@ -30,7 +30,7 @@
"test": "umi test",
"test:all": "node ./tests/run-tests.js",
"test:component": "umi test ./src/components",
"tsc": "tsc"
"tsc": "tsc --noEmit"
},
"lint-staged": {
"**/*.less": "stylelint --syntax less",
@@ -45,44 +45,47 @@
"not ie <= 10"
],
"dependencies": {
"@ant-design/icons": "^4.0.0",
"@ant-design/pro-layout": "^6.2.5",
"@ant-design/pro-table": "^2.4.0",
"@antv/g6": "^3.6.2",
"antd": "^4.4.0",
"@ant-design/icons": "^4.3.0",
"@ant-design/pro-descriptions": "^1.0.19",
"@ant-design/pro-form": "^1.9.0",
"@ant-design/pro-layout": "^6.6.1",
"@ant-design/pro-list": "^1.1.12",
"@ant-design/pro-table": "^2.15.1",
"@umijs/route-utils": "^1.0.33",
"antd": "^4.9.4",
"classnames": "^2.2.6",
"dva": "^2.4.1",
"dayjs": "^1.9.7",
"lodash": "^4.17.11",
"moment": "^2.25.3",
"omit.js": "^2.0.2",
"path-to-regexp": "2.4.0",
"qs": "^6.9.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react": "^17.0.0",
"react-dev-inspector": "^1.1.1",
"react-dom": "^17.0.0",
"react-helmet-async": "^1.0.4",
"umi": "^3.2.13",
"umi": "^3.2.14",
"umi-request": "^1.0.8",
"use-merge-value": "^1.0.1"
},
"devDependencies": {
"@ant-design/pro-cli": "^1.0.18",
"@ant-design/pro-cli": "^2.0.2",
"@types/classnames": "^2.2.7",
"@types/express": "^4.17.0",
"@types/history": "^4.7.2",
"@types/jest": "^26.0.0",
"@types/lodash": "^4.14.144",
"@types/qs": "^6.5.3",
"@types/react": "^16.9.17",
"@types/react-dom": "^16.8.4",
"@types/react-helmet": "^5.0.13",
"@umijs/fabric": "^2.2.0",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react-helmet": "^6.1.0",
"@umijs/fabric": "^2.3.0",
"@umijs/plugin-blocks": "^2.0.5",
"@umijs/plugin-esbuild": "^1.0.1",
"@umijs/preset-ant-design-pro": "^1.2.0",
"@umijs/preset-react": "^1.4.8",
"@umijs/preset-ui": "^2.0.9",
"@umijs/preset-dumi": "^1.1.0-rc.6",
"@umijs/preset-react": "^1.7.8",
"@umijs/yorkie": "^2.0.3",
"carlo": "^0.9.46",
"chalk": "^4.0.0",
"cross-env": "^7.0.0",
"cross-port-killer": "^1.1.1",
"detect-installer": "^1.0.1",
@@ -96,16 +99,10 @@
"prettier": "^2.0.1",
"pro-download": "1.0.1",
"puppeteer-core": "^5.0.0",
"stylelint": "^13.0.0"
"stylelint": "^13.0.0",
"typescript": "^4.1.2"
},
"engines": {
"node": ">=10.0.0"
},
"checkFiles": [
"src/**/*.js*",
"src/**/*.ts*",
"src/**/*.less",
"config/**/*.js*",
"scripts/**/*.js"
]
}
}

View File

@@ -1 +0,0 @@
preview.pro.ant.design

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" version="1.1" viewBox="0 0 200 200"><title>Group 28 Copy 5</title><desc>Created with Sketch.</desc><defs><linearGradient id="linearGradient-1" x1="62.102%" x2="108.197%" y1="0%" y2="37.864%"><stop offset="0%" stop-color="#4285EB"/><stop offset="100%" stop-color="#2EC7FF"/></linearGradient><linearGradient id="linearGradient-2" x1="69.644%" x2="54.043%" y1="0%" y2="108.457%"><stop offset="0%" stop-color="#29CDFF"/><stop offset="37.86%" stop-color="#148EFF"/><stop offset="100%" stop-color="#0A60FF"/></linearGradient><linearGradient id="linearGradient-3" x1="69.691%" x2="16.723%" y1="-12.974%" y2="117.391%"><stop offset="0%" stop-color="#FA816E"/><stop offset="41.473%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient><linearGradient id="linearGradient-4" x1="68.128%" x2="30.44%" y1="-35.691%" y2="114.943%"><stop offset="0%" stop-color="#FA8E7D"/><stop offset="51.264%" stop-color="#F74A5C"/><stop offset="100%" stop-color="#F51D2C"/></linearGradient></defs><g id="Page-1" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="logo" transform="translate(-20.000000, -20.000000)"><g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)"><g id="Group-27-Copy-3"><g id="Group-25" fill-rule="nonzero"><g id="2"><path id="Shape" fill="url(#linearGradient-1)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/><path id="Shape" fill="url(#linearGradient-2)" d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z"/></g><path id="Shape" fill="url(#linearGradient-3)" d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z"/></g><ellipse id="Combined-Shape" cx="100.519" cy="100.437" fill="url(#linearGradient-4)" rx="23.6" ry="23.581"/></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,5 @@
<svg width="42" height="42" xmlns="http://www.w3.org/2000/svg">
<g>
<path fill="#070707" d="m6.717392,13.773912l5.6,0c2.8,0 4.7,1.9 4.7,4.7c0,2.8 -2,4.7 -4.9,4.7l-2.5,0l0,4.3l-2.9,0l0,-13.7zm2.9,2.2l0,4.9l1.9,0c1.6,0 2.6,-0.9 2.6,-2.4c0,-1.6 -0.9,-2.4 -2.6,-2.4l-1.9,0l0,-0.1zm8.9,11.5l2.7,0l0,-5.7c0,-1.4 0.8,-2.3 2.2,-2.3c0.4,0 0.8,0.1 1,0.2l0,-2.4c-0.2,-0.1 -0.5,-0.1 -0.8,-0.1c-1.2,0 -2.1,0.7 -2.4,2l-0.1,0l0,-1.9l-2.7,0l0,10.2l0.1,0zm11.7,0.1c-3.1,0 -5,-2 -5,-5.3c0,-3.3 2,-5.3 5,-5.3s5,2 5,5.3c0,3.4 -1.9,5.3 -5,5.3zm0,-2.1c1.4,0 2.2,-1.1 2.2,-3.2c0,-2 -0.8,-3.2 -2.2,-3.2c-1.4,0 -2.2,1.2 -2.2,3.2c0,2.1 0.8,3.2 2.2,3.2z" class="st0" id="Ant-Design-Pro"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 677 B

4
dashboard/src/access.ts Normal file
View File

@@ -0,0 +1,4 @@
// src/access.ts
export default function access() {
return {};
}

79
dashboard/src/app.tsx Normal file
View File

@@ -0,0 +1,79 @@
import React from 'react';
import { notification } from 'antd';
import { RequestConfig, RunTimeLayoutConfig } from 'umi';
import { ResponseError } from 'umi-request';
import Footer from '@/components/Footer';
import RightContent from '@/components/RightContent';
import { PageLoading, Settings as LayoutSettings } from '@ant-design/pro-layout';
import defaultSettings from '../config/defaultSettings';
export const initialStateConfig = {
loading: <PageLoading />,
};
export async function getInitialState(): Promise<{
settings?: LayoutSettings;
}> {
return {
settings: defaultSettings,
};
}
export const layout: RunTimeLayoutConfig = ({ initialState }) => {
return {
rightContentRender: () => <RightContent />,
disableContentMargin: false,
footerRender: () => <Footer />,
menuHeaderRender: undefined,
// custom 403 page
// unAccessible: <div>unAccessible</div>,
...initialState?.settings,
};
};
const codeMessage = {
200: 'The server successfully returned the requested data. ',
201: 'Create or modify data successfully. ',
202: 'A request has entered the background queue (asynchronous task). ',
204: 'Delete data successfully. ',
400: 'There was an error in the request sent, and the server did not create or modify data. ',
401: 'The user does not have permission (the token, username, password are wrong). ',
403: 'The user is authorized, but access is forbidden. ',
404: 'The request sent is for a record that does not exist, and the server is not operating. ',
405: 'The requested method is not allowed. ',
406: 'The requested format is not available. ',
410: 'The requested resource has been permanently deleted and will no longer be available. ',
422: 'When creating an object, a validation error occurred. ',
500: 'An error occurred in the server, please check the server. ',
502: 'Gateway error. ',
503: 'The service is unavailable, the server is temporarily overloaded or maintained. ',
504: 'The gateway has timed out. ',
};
const errorHandler = (error: ResponseError) => {
const { response, data } = error;
if (response && response.status) {
const errorText = data?.data || codeMessage[response.status] || response.statusText;
const { status, url } = response;
notification.error({
message: `Request error ${status}: ${url}`,
description: errorText,
});
}
if (!response) {
notification.error({
description: 'Your network is abnormal and cannot connect to the server',
message: 'Network failure',
});
}
throw error;
};
export const request: RequestConfig = {
errorHandler,
};

View File

@@ -1,148 +0,0 @@
import { Form, Input, Select } from 'antd';
import { connect } from 'dva';
import React from 'react';
import _ from 'lodash';
const { Option } = Select;
const layout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
@connect(() => ({}))
export default class CreateTraitItem extends React.PureComponent {
formRefStep2 = React.createRef();
constructor(props) {
super(props);
this.state = {
parameters: [],
};
}
componentDidMount() {
this.props.onRef(this);
if (this.props.initialValues && this.props.initialValues.name) {
this.traitSelectChange(this.props.initialValues.name, 2);
}
}
getSelectValue = () => {
return this.formRefStep2.current.getFieldsValue();
};
resetFields = () => {
return this.formRefStep2.current.resetFields();
};
validateFields = () => {
return this.formRefStep2.current.validateFields();
};
setDefaultValue = (traitType) => {
this.formRefStep2.current.setFieldsValue({ name: traitType });
this.traitSelectChange(traitType);
};
traitSelectChange = async (value, isType = 1) => {
if (value) {
const res = await this.props.dispatch({
type: 'trait/getTraitByName',
payload: {
traitName: value,
},
});
this.setState({
parameters: res.parameters,
});
if (isType === 2) {
this.formRefStep2.current.setFieldsValue(this.props.initialValues);
} else if (isType) {
// 进行默认值填写
const parameters = _.get(res, 'parameters', []);
if (parameters.length) {
const initialObj = {};
parameters.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
this.formRefStep2.current.setFieldsValue(initialObj);
}
}
}
};
render() {
const { availableTraitList } = this.props;
return (
<Form
labelAlign="left"
{...layout}
ref={this.formRefStep2}
name="control-ref"
className="traitItem"
>
<Form.Item
name="name"
label="Trait"
rules={[{ required: true, message: 'Please Select a Trait!' }]}
>
<Select placeholder="Select a Trait" onChange={this.traitSelectChange}>
{availableTraitList.map((item) => {
return (
<Option value={item.name} key={item.name}>
{item.name}
</Option>
);
})}
</Select>
</Form.Item>
<Form.Item label="Properties" />
<div className="relativeBox">
{this.state.parameters ? (
this.state.parameters.map((item) => {
return item.type === 4 ? (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[0-9]*$/, message: `${item.name} only use digits(0-9).` },
]}
>
<Input />
</Form.Item>
) : (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
]}
>
<Input />
</Form.Item>
);
})
) : (
<></>
)}
</div>
</Form>
);
}
}

View File

@@ -0,0 +1,5 @@
import React from 'react';
import { DefaultFooter } from '@ant-design/pro-layout';
export default () => <DefaultFooter copyright="2020 All Rights Reserved by KubeVela" links={[]} />;

View File

@@ -1,36 +0,0 @@
import { Tag } from 'antd';
import React from 'react';
import { connect } from 'umi';
import WorkSpaceDropDown from './WorkSpaceDropDown';
import styles from './index.less';
const ENVTagColor = {
dev: 'orange',
test: 'green',
pre: '#87d068',
};
const GlobalHeaderRight = (props) => {
const { theme, layout } = props;
let className = styles.right;
if (theme === 'dark' && layout === 'top') {
className = `${styles.right} ${styles.dark}`;
}
return (
<div className={className}>
{REACT_APP_ENV && (
<span>
<Tag color={ENVTagColor[REACT_APP_ENV]}>{REACT_APP_ENV}</Tag>
</span>
)}
<WorkSpaceDropDown />
</div>
);
};
export default connect(({ settings }) => ({
theme: settings.navTheme,
layout: settings.layout,
}))(GlobalHeaderRight);

View File

@@ -1,96 +0,0 @@
.ant-pro-global-header-layout-side {
padding-right: 0;
}
.ant-dropdown-menu-item,
.ant-dropdown-menu-submenu-title {
clear: both;
margin: 0;
padding: 5px 20px;
color: deepskyblue;
font-weight: normal;
font-size: 14px;
line-height: 22px;
white-space: nowrap;
cursor: pointer;
transition: all 0.3s;
border-top: 1px solid #dee4e6;
}
.ant-dropdown-menu-item:nth-child(1) {
border-top: 1px solid #fff;
}
.ant-dropdown-menu-item-active {
background: rgb(0, 21, 41) !important;
}
.ant-dropdown-menu-item-active .box1 {
color: white;
}
.ant-dropdown-menu-item-active .box2 {
color: white;
}
.box {
display: flex;
width: 100%;
flex-direction: column;
}
.box1 {
font-size: 16px;
color: #5095d4;
}
.box2 {
font-size: 12px;
color: #8d959b;
}
.ant-dropdown-trigger {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 100%;
}
.drop-box {
display: flex;
flex-direction: row;
align-items: center;
justify-content: left;
height: 100%;
background: rgb(0, 21, 41);
padding: 0 20px;
}
.btn-box {
display: flex;
flex-direction: column;
align-items: space-between;
overflow: hidden;
margin-right: 20px;
min-width: 120px;
}
.btn-top {
display: flex;
height: 20px;
line-height: 20px;
font-size: 20px;
color: white;
}
.btn-bottom {
display: flex;
height: 20px;
line-height: 20px;
font-size: 12px;
color: #8d959b;
}
.btn-outlined {
fontsize: 15px;
color: #fff;
}

View File

@@ -1,98 +0,0 @@
import { Menu, Dropdown, message } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import React from 'react';
import { connect } from 'dva';
import './WorkSpaceDropDown.css';
@connect((env) => ({ envs: env.envs }))
export default class WorkSpaceDropDown extends React.Component {
constructor(props) {
super(props);
this.state = {
workSpaceName: '',
namespace: '',
};
}
async componentDidMount() {
const envs = await this.props.dispatch({
type: 'envs/getEnvs',
});
if (envs) {
const { envName, namespace } = envs.find((env) => {
return env.current === '*';
});
this.setState({
workSpaceName: envName,
namespace,
});
this.props.dispatch({
type: 'globalData/currentEnv',
payload: {
currentEnv: envName,
},
});
}
}
handleMenuClick = async (e) => {
// 发送切换envs的接口
const switchResult = await this.props.dispatch({
type: 'envs/switchEnv',
payload: {
currentEnv: e.key,
},
});
if (switchResult) {
message.success(switchResult);
}
this.setState(
{
workSpaceName: e.key,
namespace: e.item.props.title,
},
() => {
// 值切换存储
this.props.dispatch({
type: 'globalData/currentEnv',
payload: {
currentEnv: e.key,
},
});
},
);
await this.props.dispatch({
type: 'envs/getEnvs', // applist对应models层的命名空间namespace
});
};
render() {
const { envs } = this.props;
const menu = (
<Menu onClick={this.handleMenuClick}>
{envs.envs &&
envs.envs.map((item) => {
return (
<Menu.Item key={item.envName} title={item.namespace}>
<div className="box">
<div className="box1">{item.envName}</div>
<div className="box2">{item.namespace}</div>
</div>
</Menu.Item>
);
})}
</Menu>
);
return (
<Dropdown overlay={menu}>
<div className="drop-box">
<div className="btn-box">
<div className="btn-top">{this.state.workSpaceName}</div>
<div className="btn-bottom">{this.state.namespace}</div>
</div>
<DownOutlined style={{ fontSize: '15px', color: '#ffffff' }} />
</div>
</Dropdown>
);
}
}

View File

@@ -0,0 +1,16 @@
@import '~antd/es/style/themes/default.less';
.container > * {
background-color: @popover-bg;
border-radius: 4px;
box-shadow: @shadow-1-down;
}
@media screen and (max-width: @screen-xs) {
.container {
width: 100% !important;
}
.container > * {
border-radius: 0 !important;
}
}

View File

@@ -0,0 +1,21 @@
import React from 'react';
import { Dropdown } from 'antd';
import { DropDownProps } from 'antd/es/dropdown';
import classNames from 'classnames';
import styles from './index.less';
declare type OverlayFunc = () => React.ReactNode;
export interface HeaderDropdownProps extends Omit<DropDownProps, 'overlay'> {
overlayClassName?: string;
overlay: React.ReactNode | OverlayFunc | any;
placement?: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight' | 'bottomCenter';
}
const HeaderDropdown: React.FC<HeaderDropdownProps> = ({ overlayClassName: cls, ...restProps }) => (
<Dropdown overlayClassName={classNames(styles.container, cls)} {...restProps} />
);
export default HeaderDropdown;

View File

@@ -1,4 +0,0 @@
import { PageLoading } from '@ant-design/pro-layout'; // loading components from code split
// https://umijs.org/plugin/umi-plugin-react.html#dynamicimport
export default PageLoading;

View File

@@ -0,0 +1,66 @@
import React from 'react';
import { Menu, message, Spin, Typography } from 'antd';
import { useModel } from 'umi';
import { DownOutlined } from '@ant-design/icons';
import HeaderDropdown from '../HeaderDropdown';
import styles from './index.less';
export default () => {
const {
environments,
currentEnvironment,
switchCurrentEnvironment: switchEnvironment,
} = useModel('useEnvironmentModel');
const menu = (
<Menu
className={styles.menu}
selectedKeys={currentEnvironment == null ? undefined : [currentEnvironment.envName]}
onClick={(e) => {
switchEnvironment(e.key.toString()).then((env) => {
if (env == null) {
return;
}
message.success({
content: `Set environment succeed, current environment is ${env.envName}, namespace is ${env.namespace}`,
key: 'switchEnvironment',
});
});
}}
>
{environments &&
environments.map((item) => {
return (
<Menu.Item key={item.envName} title={item.namespace}>
<div>
<Typography.Text>{item.envName}</Typography.Text>
</div>
<div>
<Typography.Text type="secondary">
<small>{item.namespace}</small>
</Typography.Text>
</div>
</Menu.Item>
);
})}
</Menu>
);
return (
<HeaderDropdown overlay={menu}>
<div className={`${styles.action}`}>
{environments == null || currentEnvironment == null ? (
<Spin size="small" />
) : (
<>
<span className={`${styles.name} anticon`}>{currentEnvironment?.envName}</span>
<DownOutlined style={{ marginLeft: '5px' }} />
</>
)}
</div>
</HeaderDropdown>
);
};

View File

@@ -20,7 +20,7 @@
.action {
display: flex;
align-items: center;
height: 100%;
height: 48px;
padding: 0 12px;
cursor: pointer;
transition: all 0.3s;
@@ -42,7 +42,6 @@
}
.account {
.avatar {
margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0;
margin-right: 8px;
color: @primary-color;
vertical-align: top;
@@ -53,52 +52,11 @@
.dark {
.action {
color: rgba(255, 255, 255, 0.85);
> span {
color: rgba(255, 255, 255, 0.85);
&:hover {
background: #252a3d;
}
&:hover,
&:global(.opened) {
background: @primary-color;
}
}
}
:global(.ant-pro-global-header) {
.dark {
.action {
color: @text-color;
> span {
color: @text-color;
}
&:hover {
color: rgba(255, 255, 255, 0.85);
> span {
color: rgba(255, 255, 255, 0.85);
}
}
}
}
}
@media only screen and (max-width: @screen-md) {
:global(.ant-divider-vertical) {
vertical-align: unset;
}
.name {
display: none;
}
.right {
position: absolute;
top: 0;
right: 12px;
.account {
.avatar {
margin-right: 0;
}
}
.search {
display: none;
background: #252a3d;
}
}
}

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { Space, Tag } from 'antd';
import { SelectLang, useModel } from 'umi';
import styles from './index.less';
import WorkSpaceDropDown from './WorkSpaceDropDown';
export type SiderTheme = 'light' | 'dark';
const ENVTagColor = {
dev: 'orange',
test: 'green',
pre: '#87d068',
};
const GlobalHeaderRight: React.FC<{}> = () => {
const { initialState } = useModel('@@initialState');
if (!initialState || !initialState.settings) {
return null;
}
const { navTheme, layout } = initialState.settings;
let className = styles.right;
if ((navTheme === 'dark' && layout === 'top') || layout === 'mix') {
className = `${styles.right} ${styles.dark}`;
}
return (
<Space className={className}>
{REACT_APP_ENV && (
<span>
<Tag color={ENVTagColor[REACT_APP_ENV]}>{REACT_APP_ENV}</Tag>
</span>
)}
<WorkSpaceDropDown />
<SelectLang className={styles.action} />
</Space>
);
};
export default GlobalHeaderRight;

View File

@@ -1,362 +0,0 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Row, Col, Modal, Select, message, Breadcrumb, Form, Input } from 'antd';
import './index.less';
import { connect } from 'dva';
import { Link } from 'umi';
import _ from 'lodash';
const { Option } = Select;
const layout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
@connect(({ loading, applist, globalData }) => ({
loadingAll: loading.models.applist,
currentEnv: globalData.currentEnv,
returnObj: applist.returnObj,
}))
class Trait extends React.Component {
formRefStep2 = React.createRef();
constructor(props) {
super(props);
this.state = {
visible: false,
selectValue: null,
compList: [],
};
}
componentDidMount() {
this.getInitialData();
}
shouldComponentUpdate(nextProps) {
if (nextProps.currentEnv === this.props.currentEnv) {
return true;
}
this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${nextProps.currentEnv}/apps/`,
},
});
return true;
}
getInitialData = async () => {
if (this.props.currentEnv) {
await this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${this.props.currentEnv}/apps/`,
},
});
}
};
showModal = () => {
this.setState(
{
visible: true,
},
() => {
if (this.formRefStep2.current) {
this.formRefStep2.current.resetFields();
}
},
);
};
handleOk = async () => {
await this.formRefStep2.current.validateFields();
const { title } = this.props.propsObj;
if (title) {
const submitObj = {
name: title,
flags: [],
};
const submitData = this.formRefStep2.current.getFieldValue();
Object.keys(submitData).forEach((currentKey) => {
if (
currentKey !== 'name' &&
currentKey !== 'appName' &&
currentKey !== 'compName' &&
submitData[currentKey]
) {
submitObj.flags.push({
name: currentKey,
value: submitData[currentKey].toString(),
});
}
});
const { currentEnv: envName } = this.props;
const { appName, compName } = submitData;
if (envName && appName && compName) {
const res = await this.props.dispatch({
type: 'trait/attachOneTraits',
payload: {
envName,
appName,
compName,
params: submitObj,
},
});
if (res) {
this.setState({
visible: false,
});
message.success(res);
const { history } = this.props.propsObj;
history.push({
pathname: `/ApplicationList/${appName}/Components`,
state: { appName, envName },
});
}
}
}
};
handleCancel = () => {
this.setState({
visible: false,
});
};
onChange = async (value) => {
this.setState({
selectValue: value,
compList: [],
});
const res = await this.props.dispatch({
type: 'applist/getAppDetail',
payload: {
envName: this.props.currentEnv,
appName: value,
},
});
if (res) {
const compData = _.get(res, 'components', []);
const compList = [];
compData.forEach((item) => {
compList.push({
compName: item.name,
});
});
this.setState({
compList,
});
}
};
onSearch = () => {};
render() {
const { btnValue, title, settings = [], btnIsShow, crdInfo, appliesTo } = this.props.propsObj;
const initialObj = {};
if (settings.length) {
settings.forEach((item) => {
if (item.default) {
initialObj[item.name] = item.default;
}
});
}
let appList = _.get(this.props, 'returnObj', []);
if (!appList) {
appList = [];
}
const { compList = [] } = this.state;
return (
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Traits</Breadcrumb.Item>
<Breadcrumb.Item>{title}</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Row>
<Col span="11">
<div className="deployment">
<Row>
<Col span="22">
<p className="title">{title}</p>
{crdInfo ? (
<p>
{crdInfo.apiVersion}
<span>,kind=</span>
{crdInfo.kind}
</p>
) : (
<p />
)}
</Col>
</Row>
<Row>
<Col span="22">
<p className="title">Applies To:</p>
<p>{Array.isArray(appliesTo) ? appliesTo.join(', ') : appliesTo}</p>
</Col>
</Row>
<p className="title">Configurable Properties:</p>
{settings.map((item, index) => {
return (
<Row key={index.toString()}>
<Col span="8">
<p>{item.name}</p>
</Col>
<Col span="16">
<p>{item.default || item.usage}</p>
</Col>
</Row>
);
})}
</div>
<Button
type="primary"
className="create-button"
onClick={this.showModal}
style={{ display: btnIsShow ? 'block' : 'none' }}
>
{btnValue}
</Button>
<Modal
title="Attach Trait"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Submit
</Button>,
]}
>
<Form
labelAlign="left"
{...layout}
ref={this.formRefStep2}
name="control-ref"
className="traitItem"
initialValues={initialObj}
>
<Form.Item
label="App"
name="appName"
rules={[{ required: true, message: 'Please Select a Application!' }]}
>
<Select
showSearch
value={this.state.selectValue}
style={{ width: '100%' }}
placeholder="Select a Application"
optionFilterProp="children"
onChange={this.onChange}
onSearch={this.onSearch}
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{appList.length ? (
appList.map((item, index) => {
return (
<Option key={index.toString()} value={item.name}>
{item.name}
</Option>
);
})
) : (
<Fragment />
)}
</Select>
</Form.Item>
<Form.Item
label="Component"
name="compName"
rules={[{ required: true, message: 'Please Select a Component!' }]}
>
<Select
allowClear
// value={this.state.selectValue}
style={{ width: '100%' }}
placeholder="Select a Component"
>
{compList.length ? (
compList.map((item) => {
return (
<Option key={item.compName} value={item.compName}>
{item.compName}
</Option>
);
})
) : (
<Fragment />
)}
</Select>
</Form.Item>
<div className="relativeBox">
<Form.Item label="Properties" />
{settings ? (
settings.map((item) => {
return item.type === 4 ? (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{
pattern: /^[0-9]*$/,
message: `${item.name} only use digits(0-9).`,
},
]}
>
<Input />
</Form.Item>
) : (
<Form.Item
name={item.name}
label={item.name}
key={item.name}
rules={[
{
required: item.required || false,
message: `Please input ${item.name} !`,
},
{ pattern: /^[^\s]*$/, message: 'Spaces are not allowed!' },
]}
>
<Input />
</Form.Item>
);
})
) : (
<></>
)}
</div>
</Form>
</Modal>
</Col>
</Row>
</PageContainer>
</div>
);
}
}
export default Trait;

View File

@@ -1,27 +0,0 @@
.deployment {
position: relative;
padding: 10px;
padding-left: 16px;
background-color: #fff;
border: 1px solid #eee;
a {
position: absolute;
top: 10px;
right: 16px;
font-size: 20px;
}
.title {
margin: 0;
font-size: 18px;
line-height: 36px;
}
p {
font-size: 12px;
line-height: 20px;
}
}
.create-button {
float: right;
margin-top: 16px;
text-align: right;
}

View File

@@ -1,226 +0,0 @@
import React, { Fragment } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Row, Col, Modal, Select, Breadcrumb, Form } from 'antd';
import { connect } from 'dva';
import { Link } from 'umi';
import _ from 'lodash';
import './index.less';
const { Option } = Select;
const layout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
@connect(({ loading, applist, globalData }) => ({
loadingAll: loading.models.applist,
currentEnv: globalData.currentEnv,
returnObj: applist.returnObj,
}))
export default class Workload extends React.Component {
formRefStep2 = React.createRef();
constructor(props) {
super(props);
this.state = {
visible: false,
};
}
componentDidMount() {
this.getInitialData();
}
shouldComponentUpdate(nextProps) {
if (nextProps.currentEnv === this.props.currentEnv) {
return true;
}
this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${nextProps.currentEnv}/apps/`,
},
});
return true;
}
getInitialData = async () => {
if (this.props.currentEnv) {
await this.props.dispatch({
type: 'applist/getList',
payload: {
url: `/api/envs/${this.props.currentEnv}/apps/`,
},
});
}
};
showModal = () => {
this.setState(
{
visible: true,
},
() => {
if (this.formRefStep2.current) {
this.formRefStep2.current.resetFields();
}
},
);
};
handleOk = async () => {
const submitData = await this.formRefStep2.current.validateFields();
const { history } = this.props.propsObj;
history.push({
pathname: `/ApplicationList/${submitData.appName}/createComponent`,
state: {
...submitData,
isCreate: false,
envName: this.props.currentEnv,
WorkloadType: this.props.propsObj.title,
},
});
};
handleCancel = () => {
this.setState({
visible: false,
});
};
onChange = () => {};
onSearch = () => {};
render() {
const { btnValue, title, crdInfo, settings, btnIsShow } = this.props.propsObj;
let appList = _.get(this.props, 'returnObj', []);
if (!appList) {
appList = [];
}
return (
<div>
<div className="breadCrumb">
<Breadcrumb>
<Breadcrumb.Item>
<Link to="/ApplicationList">Home</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Workloads</Breadcrumb.Item>
<Breadcrumb.Item>{title}</Breadcrumb.Item>
</Breadcrumb>
</div>
<PageContainer>
<Row>
<Col span="11">
<div className="deployment">
<Row>
<Col span="22">
<p className="title">{title}</p>
{crdInfo ? (
<p>
{crdInfo.apiVersion}
<span>,kind=</span>
{crdInfo.kind}
</p>
) : (
<p />
)}
</Col>
</Row>
<p className="title">Configurable Settings:</p>
{settings.map((item, index) => {
if (item.name === 'name') {
return <Fragment key={index.toString()} />;
}
return (
<Row key={index.toString()}>
<Col span="8">
<p>{item.name}</p>
</Col>
<Col span="16">
{
// eslint-disable-next-line consistent-return
}
<p>{item.default || item.usage}</p>
</Col>
</Row>
);
})}
</div>
{/* <Link to={{ pathname, state }} style={{ display: btnIsShow ? 'block' : 'none' }}>
<Button type="primary" className="create-button">
{btnValue}
</Button>
</Link> */}
<Button
type="primary"
className="create-button"
onClick={() => this.showModal()}
style={{ display: btnIsShow ? 'block' : 'none' }}
>
{btnValue}
</Button>
</Col>
</Row>
<Modal
title="Add Component"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={[
<Button key="back" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleOk}>
Next
</Button>,
]}
>
<Form
labelAlign="left"
{...layout}
ref={this.formRefStep2}
name="control-ref"
className="traitItem"
>
<Form.Item
label="App"
name="appName"
rules={[{ required: true, message: 'Please Select a Application!' }]}
>
<Select
showSearch
style={{ width: '100%' }}
placeholder="Select a Application"
optionFilterProp="children"
onChange={this.onChange}
onSearch={this.onSearch}
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{appList.length ? (
appList.map((item, index) => {
return (
<Option key={index.toString()} value={item.name}>
{item.name}
</Option>
);
})
) : (
<Fragment />
)}
</Select>
</Form.Item>
</Form>
</Modal>
</PageContainer>
</div>
);
}
}

View File

@@ -1,27 +0,0 @@
.deployment {
position: relative;
padding: 10px;
padding-left: 16px;
background-color: #fff;
border: 1px solid #eee;
a {
position: absolute;
top: 10px;
right: 16px;
font-size: 20px;
}
.title {
margin: 0;
font-size: 18px;
line-height: 36px;
}
p {
font-size: 12px;
line-height: 20px;
}
}
.create-button {
float: right;
margin-top: 16px;
text-align: right;
}

View File

@@ -1 +0,0 @@
export default undefined;

View File

@@ -29,6 +29,13 @@ beforeEach(async () => {
describe('Ant Design Pro E2E test', () => {
const testPage = (path) => async () => {
await page.goto(`${BASE_URL}${path}`);
await page.waitForSelector('footer', {
timeout: 2000,
});
const haveFooter = await page.evaluate(
() => document.getElementsByTagName('footer').length > 0,
);
expect(haveFooter).toBeTruthy();
};
const routers = formatter(RouterConfig);
@@ -39,5 +46,12 @@ describe('Ant Design Pro E2E test', () => {
it('topmenu should have footer', async () => {
const params = '?navTheme=light&layout=topmenu';
await page.goto(`${BASE_URL}${params}`);
await page.waitForSelector('footer', {
timeout: 2000,
});
const haveFooter = await page.evaluate(
() => document.getElementsByTagName('footer').length > 0,
);
expect(haveFooter).toBeTruthy();
});
});

View File

@@ -13,9 +13,6 @@ body,
.ant-layout {
min-height: 100vh;
}
.ant-card {
margin-bottom: 30px !important;
}
canvas {
display: block;
@@ -55,49 +52,3 @@ ol {
min-height: 100vh;
}
}
// 首页导航标题样式
#logo {
padding: 16px 8px;
img {
display: none;
}
}
// 面包屑样式调整
.ant-page-header {
padding: 12px 24px !important;
}
.ant-page-header-heading {
display: none !important;
}
.ant-pro-page-container-warp {
display: none;
}
.ant-pro-basicLayout-content {
margin: 0 !important;
}
.ant-pro-basicLayout-content .ant-pro-page-container {
margin: 0 !important;
}
.breadCrumb {
padding: 12px 24px;
background: #fff;
}
.ant-breadcrumb a:hover {
color: #1b58f4 !important;
}
// 对齐
.ant-form-item-label > label::before {
display: inline-block;
width: 7.09px;
height: 14px;
margin-right: 4px;
color: rgb(255, 77, 79);
font-size: 14px;
font-family: SimSun, sans-serif;
line-height: 1;
content: '';
}
.ant-spin-nested-loading {
height: calc(100% - 46px) !important;
}

View File

@@ -1,35 +1,32 @@
import { Button, message, notification } from 'antd';
import React from 'react';
import { useIntl } from 'umi';
import defaultSettings from '../config/defaultSettings';
const { pwa } = defaultSettings; // if pwa is true
const { pwa } = defaultSettings;
const isHttps = document.location.protocol === 'https:';
// if pwa is true
if (pwa) {
// Notify user if offline now
window.addEventListener('sw.offline', () => {
message.warning(
useIntl().formatMessage({
id: 'app.pwa.offline',
}),
);
}); // Pop up a prompt on the page asking the user if they want to use the latest version
window.addEventListener('sw.updated', (event) => {
const e = event;
message.warning(useIntl().formatMessage({ id: 'app.pwa.offline' }));
});
// Pop up a prompt on the page asking the user if they want to use the latest version
window.addEventListener('sw.updated', (event: Event) => {
const e = event as CustomEvent;
const reloadSW = async () => {
// Check if there is sw whose state is waiting in ServiceWorkerRegistration
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
const worker = e.detail && e.detail.waiting;
if (!worker) {
return true;
} // Send skip-waiting event to waiting SW with MessageChannel
}
// Send skip-waiting event to waiting SW with MessageChannel
await new Promise((resolve, reject) => {
const channel = new MessageChannel();
channel.port1.onmessage = (msgEvent) => {
if (msgEvent.data.error) {
reject(msgEvent.data.error);
@@ -37,19 +34,12 @@ if (pwa) {
resolve(msgEvent.data);
}
};
worker.postMessage(
{
type: 'skip-waiting',
},
[channel.port2],
);
}); // Refresh current page to use the updated HTML and other assets after SW has skiped waiting
worker.postMessage({ type: 'skip-waiting' }, [channel.port2]);
});
// Refresh current page to use the updated HTML and other assets after SW has skiped waiting
window.location.reload(true);
return true;
};
const key = `open${Date.now()}`;
const btn = (
<Button
@@ -59,27 +49,20 @@ if (pwa) {
reloadSW();
}}
>
{useIntl().formatMessage({
id: 'app.pwa.serviceworker.updated.ok',
})}
{useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.ok' })}
</Button>
);
notification.open({
message: useIntl().formatMessage({
id: 'app.pwa.serviceworker.updated',
}),
description: useIntl().formatMessage({
id: 'app.pwa.serviceworker.updated.hint',
}),
message: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated' }),
description: useIntl().formatMessage({ id: 'app.pwa.serviceworker.updated.hint' }),
btn,
key,
onClose: async () => {},
});
});
} else if ('serviceWorker' in navigator) {
} else if ('serviceWorker' in navigator && isHttps) {
// unregister service worker
const { serviceWorker } = navigator;
if (serviceWorker.getRegistrations) {
serviceWorker.getRegistrations().then((sws) => {
sws.forEach((sw) => {
@@ -87,11 +70,11 @@ if (pwa) {
});
});
}
serviceWorker.getRegistration().then((sw) => {
if (sw) sw.unregister();
}); // remove all caches
});
// remove all caches
if (window.caches && window.caches.keys) {
caches.keys().then((keys) => {
keys.forEach((key) => {

View File

@@ -1,137 +0,0 @@
/**
* Ant Design Pro v4 use `@ant-design/pro-layout` to handle Layout.
* You can view component api by:
* https://github.com/ant-design/ant-design-pro-layout
*/
import ProLayout from '@ant-design/pro-layout';
import React, { useEffect, useState, useRef } from 'react';
import { Link, useIntl, connect, history } from 'umi';
import RightContent from '@/components/GlobalHeader/RightContent';
import {
MenuOutlined,
BranchesOutlined,
ApartmentOutlined,
DeploymentUnitOutlined,
SettingOutlined,
} from '@ant-design/icons';
import _ from 'lodash';
const AddIcon = (menuData) => {
return menuData.map((item) => {
const name = _.get(item, 'name', '');
if (name) {
if (name === 'Workload') {
// eslint-disable-next-line no-param-reassign
item.icon = <ApartmentOutlined />;
} else if (name === 'Traits') {
// eslint-disable-next-line no-param-reassign
item.icon = <BranchesOutlined />;
} else if (name === 'Capability') {
// eslint-disable-next-line no-param-reassign
item.icon = <DeploymentUnitOutlined />;
} else if (name === 'System') {
// eslint-disable-next-line no-param-reassign
item.icon = <SettingOutlined />;
} else {
// eslint-disable-next-line no-param-reassign
item.icon = <MenuOutlined />;
}
}
return item;
});
};
const BasicLayout = (props) => {
const { settings, dispatch, menus } = props;
const [currentSelectKeys, setCurrentSelectedKeys] = useState('');
const timerRef = useRef();
const getCurrentSelectKeys = () => {
const pathnameCur = props.history.location.pathname;
if (pathnameCur) {
if (pathnameCur.includes('Application')) {
setCurrentSelectedKeys(['applist']);
} else if (pathnameCur.includes('Capability')) {
setCurrentSelectedKeys(['Capability']);
} else if (pathnameCur.includes('System/Env')) {
setCurrentSelectedKeys(['Env']);
} else if (pathnameCur.includes('Workload')) {
const arr = pathnameCur.split('/');
const key = arr[arr.length - 1];
setCurrentSelectedKeys([key]);
} else if (pathnameCur.includes('Traits')) {
const arr = pathnameCur.split('/');
const key = arr[arr.length - 1];
setCurrentSelectedKeys([key]);
}
}
};
useEffect(() => {
if (dispatch) {
dispatch({
type: 'menus/getMenuData',
});
}
timerRef.current = props.history.listen((route) => {
getCurrentSelectKeys(route.pathname);
});
return () => {
if (timerRef.current) {
timerRef.current = null;
}
};
// setCurrentSelectedKeys('applist')
}, []);
const { formatMessage } = useIntl();
return (
<ProLayout
formatMessage={formatMessage}
onMenuHeaderClick={() => history.push('/')}
menuItemRender={(menuItemProps, defaultDom) => {
if (menuItemProps.isUrl || !menuItemProps.path) {
return defaultDom;
}
// return <Link to={menuItemProps.path}>{defaultDom}</Link>;
return (
<div
onClick={() => {
setCurrentSelectedKeys([menuItemProps.key]);
history.push(menuItemProps.path);
}}
>
{defaultDom}
</div>
);
}}
selectedKeys={currentSelectKeys}
breadcrumbRender={(routers = []) => [
{
path: '/',
breadcrumbName: formatMessage({
id: 'menu.home',
}),
},
...routers,
]}
itemRender={(route, params, routes, paths) => {
const first = routes.indexOf(route) === 0;
return first ? (
<Link to={paths.join('/')}>{route.breadcrumbName}</Link>
) : (
<span>{route.breadcrumbName}</span>
);
}}
// menuDataRender={menuDataRender}
menuDataRender={() => AddIcon(menus.menuData)}
rightContentRender={() => <RightContent />}
{...props}
{...settings}
/>
);
};
export default connect(({ global, settings, menus }) => ({
global,
settings,
menus,
}))(BasicLayout);

View File

@@ -1,13 +0,0 @@
import React from 'react';
import { connect } from 'umi';
class SecurityLayout extends React.Component {
render() {
const { children } = this.props;
return children;
}
}
export default connect(({ loading }) => ({
loading: loading.models.user,
}))(SecurityLayout);

View File

@@ -1,22 +0,0 @@
import component from './en-US/component';
import globalHeader from './en-US/globalHeader';
import menu from './en-US/menu';
import pwa from './en-US/pwa';
import settingDrawer from './en-US/settingDrawer';
import settings from './en-US/settings';
export default {
'navBar.lang': 'Languages',
'layout.user.link.help': 'Help',
'layout.user.link.privacy': 'Privacy',
'layout.user.link.terms': 'Terms',
'app.preview.down.block': 'Download this page to your local project',
'app.welcome.link.fetch-blocks': 'Get all block',
'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development',
...globalHeader,
...menu,
...settingDrawer,
...settings,
...pwa,
...component,
};

View File

@@ -0,0 +1,11 @@
import component from './en-US/component';
import menu from './en-US/menu';
import pages from './en-US/pages';
import pwa from './en-US/pwa';
export default {
...menu,
...pwa,
...component,
...pages,
};

View File

@@ -1,17 +0,0 @@
export default {
'component.globalHeader.search': 'Search',
'component.globalHeader.search.example1': 'Search example 1',
'component.globalHeader.search.example2': 'Search example 2',
'component.globalHeader.search.example3': 'Search example 3',
'component.globalHeader.help': 'Help',
'component.globalHeader.notification': 'Notification',
'component.globalHeader.notification.empty': 'You have viewed all notifications.',
'component.globalHeader.message': 'Message',
'component.globalHeader.message.empty': 'You have viewed all messsages.',
'component.globalHeader.event': 'Event',
'component.globalHeader.event.empty': 'You have viewed all events.',
'component.noticeIcon.clear': 'Clear',
'component.noticeIcon.cleared': 'Cleared',
'component.noticeIcon.empty': 'No notifications',
'component.noticeIcon.view-more': 'View more',
};

View File

@@ -1,74 +0,0 @@
export default {
'menu.welcome': 'Welcome',
'menu.more-blocks': 'More Blocks',
'menu.home': 'Home',
'menu.Traits': 'Traits',
'menu.Traits.TraitItem': 'TraitItem',
'menu.Traits.Scale': 'Scale',
'menu.Traits.Rollout': 'Rollout',
'menu.Traits.Route': 'Route',
'menu.Traits.Manualscaler': 'Manualscaler',
'menu.Traits.Detail': 'Detail',
'menu.ApplicationList': 'Applications',
'menu.ApplicationList.ApplicationListDetail': 'ApplicationListDetail',
'menu.ApplicationList.CreateApplication': 'CreateApplication',
'menu.ApplicationList.WorkloadDetail': 'WorkloadDetail',
'menu.ApplicationList.TraitDetail': 'TraitDetail',
'menu.Capability': 'Capability',
'menu.Capability.Detail': 'Detail',
'menu.System': 'System',
'menu.System.Env': 'Env',
'menu.Workload': 'Workloads',
'menu.Workload.WorkloadItem': 'WorkloadItem',
'menu.Workload.Deployment': 'Deployment',
'menu.Workload.Containerized': 'Containerized',
'menu.Workload.Detail': 'Detail',
'menu.Release': 'Release',
'menu.admin': 'Admin',
'menu.admin.sub-page': 'Sub-Page',
'menu.login': 'Login',
'menu.register': 'Register',
'menu.register.result': 'Register Result',
'menu.dashboard': 'Dashboard',
'menu.dashboard.analysis': 'Analysis',
'menu.dashboard.monitor': 'Monitor',
'menu.dashboard.workplace': 'Workplace',
'menu.exception.403': '403',
'menu.exception.404': '404',
'menu.exception.500': '500',
'menu.form': 'Form',
'menu.form.basic-form': 'Basic Form',
'menu.form.step-form': 'Step Form',
'menu.form.step-form.info': 'Step Form(write transfer information)',
'menu.form.step-form.confirm': 'Step Form(confirm transfer information)',
'menu.form.step-form.result': 'Step Form(finished)',
'menu.form.advanced-form': 'Advanced Form',
'menu.list': 'List',
'menu.list.table-list': 'Search Table',
'menu.list.basic-list': 'Basic List',
'menu.list.card-list': 'Card List',
'menu.list.search-list': 'Search List',
'menu.list.search-list.articles': 'Search List(articles)',
'menu.list.search-list.projects': 'Search List(projects)',
'menu.list.search-list.applications': 'Search List(applications)',
'menu.profile': 'Profile',
'menu.profile.basic': 'Basic Profile',
'menu.profile.advanced': 'Advanced Profile',
'menu.result': 'Result',
'menu.result.success': 'Success',
'menu.result.fail': 'Fail',
'menu.exception': 'Exception',
'menu.exception.not-permission': '403',
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': 'Trigger',
'menu.account': 'Account',
'menu.account.center': 'Account Center',
'menu.account.settings': 'Account Settings',
'menu.account.trigger': 'Trigger Error',
'menu.account.logout': 'Logout',
'menu.editor': 'Graphic Editor',
'menu.editor.flow': 'Flow Editor',
'menu.editor.mind': 'Mind Editor',
'menu.editor.koni': 'Koni Editor',
};

View File

@@ -0,0 +1,9 @@
export default {
'menu.home': 'Home',
'menu.system': 'System',
'menu.system.environment': 'Environment',
'menu.applications': 'Applications',
'menu.capability': 'Capability',
'menu.capability.workloads': 'Workloads',
'menu.capability.traits': 'Traits',
};

View File

@@ -0,0 +1,6 @@
export default {
'pages.welcome.advancedComponent': 'Advanced Component',
'pages.welcome.link': 'Welcome',
'pages.welcome.advancedLayout': 'Advanced Layout',
'pages.welcome.alertMessage': 'Faster and stronger heavy-duty components have been released.',
};

View File

@@ -1,31 +0,0 @@
export default {
'app.setting.pagestyle': 'Page style setting',
'app.setting.pagestyle.dark': 'Dark style',
'app.setting.pagestyle.light': 'Light style',
'app.setting.content-width': 'Content Width',
'app.setting.content-width.fixed': 'Fixed',
'app.setting.content-width.fluid': 'Fluid',
'app.setting.themecolor': 'Theme Color',
'app.setting.themecolor.dust': 'Dust Red',
'app.setting.themecolor.volcano': 'Volcano',
'app.setting.themecolor.sunset': 'Sunset Orange',
'app.setting.themecolor.cyan': 'Cyan',
'app.setting.themecolor.green': 'Polar Green',
'app.setting.themecolor.daybreak': 'Daybreak Blue (default)',
'app.setting.themecolor.geekblue': 'Geek Glue',
'app.setting.themecolor.purple': 'Golden Purple',
'app.setting.navigationmode': 'Navigation Mode',
'app.setting.sidemenu': 'Side Menu Layout',
'app.setting.topmenu': 'Top Menu Layout',
'app.setting.fixedheader': 'Fixed Header',
'app.setting.fixedsidebar': 'Fixed Sidebar',
'app.setting.fixedsidebar.hint': 'Works on Side Menu Layout',
'app.setting.hideheader': 'Hidden Header when scrolling',
'app.setting.hideheader.hint': 'Works when Hidden Header is enabled',
'app.setting.othersettings': 'Other Settings',
'app.setting.weakmode': 'Weak Mode',
'app.setting.copy': 'Copy Setting',
'app.setting.copyinfo': 'copy successplease replace defaultSettings in src/models/setting.js',
'app.setting.production.hint':
'Setting panel shows in development environment only, please manually modify',
};

View File

@@ -1,60 +0,0 @@
export default {
'app.settings.menuMap.basic': 'Basic Settings',
'app.settings.menuMap.security': 'Security Settings',
'app.settings.menuMap.binding': 'Account Binding',
'app.settings.menuMap.notification': 'New Message Notification',
'app.settings.basic.avatar': 'Avatar',
'app.settings.basic.change-avatar': 'Change avatar',
'app.settings.basic.email': 'Email',
'app.settings.basic.email-message': 'Please input your email!',
'app.settings.basic.nickname': 'Nickname',
'app.settings.basic.nickname-message': 'Please input your Nickname!',
'app.settings.basic.profile': 'Personal profile',
'app.settings.basic.profile-message': 'Please input your personal profile!',
'app.settings.basic.profile-placeholder': 'Brief introduction to yourself',
'app.settings.basic.country': 'Country/Region',
'app.settings.basic.country-message': 'Please input your country!',
'app.settings.basic.geographic': 'Province or city',
'app.settings.basic.geographic-message': 'Please input your geographic info!',
'app.settings.basic.address': 'Street Address',
'app.settings.basic.address-message': 'Please input your address!',
'app.settings.basic.phone': 'Phone Number',
'app.settings.basic.phone-message': 'Please input your phone!',
'app.settings.basic.update': 'Update Information',
'app.settings.security.strong': 'Strong',
'app.settings.security.medium': 'Medium',
'app.settings.security.weak': 'Weak',
'app.settings.security.password': 'Account Password',
'app.settings.security.password-description': 'Current password strength',
'app.settings.security.phone': 'Security Phone',
'app.settings.security.phone-description': 'Bound phone',
'app.settings.security.question': 'Security Question',
'app.settings.security.question-description':
'The security question is not set, and the security policy can effectively protect the account security',
'app.settings.security.email': 'Backup Email',
'app.settings.security.email-description': 'Bound Email',
'app.settings.security.mfa': 'MFA Device',
'app.settings.security.mfa-description':
'Unbound MFA device, after binding, can be confirmed twice',
'app.settings.security.modify': 'Modify',
'app.settings.security.set': 'Set',
'app.settings.security.bind': 'Bind',
'app.settings.binding.taobao': 'Binding Taobao',
'app.settings.binding.taobao-description': 'Currently unbound Taobao account',
'app.settings.binding.alipay': 'Binding Alipay',
'app.settings.binding.alipay-description': 'Currently unbound Alipay account',
'app.settings.binding.dingding': 'Binding DingTalk',
'app.settings.binding.dingding-description': 'Currently unbound DingTalk account',
'app.settings.binding.bind': 'Bind',
'app.settings.notification.password': 'Account Password',
'app.settings.notification.password-description':
'Messages from other users will be notified in the form of a station letter',
'app.settings.notification.messages': 'System Messages',
'app.settings.notification.messages-description':
'System messages will be notified in the form of a station letter',
'app.settings.notification.todo': 'To-do Notification',
'app.settings.notification.todo-description':
'The to-do list will be notified in the form of a letter from the station',
'app.settings.open': 'Open',
'app.settings.close': 'Close',
};

View File

@@ -1,20 +0,0 @@
import component from './pt-BR/component';
import globalHeader from './pt-BR/globalHeader';
import menu from './pt-BR/menu';
import pwa from './pt-BR/pwa';
import settingDrawer from './pt-BR/settingDrawer';
import settings from './pt-BR/settings';
export default {
'navBar.lang': 'Idiomas',
'layout.user.link.help': 'ajuda',
'layout.user.link.privacy': 'política de privacidade',
'layout.user.link.terms': 'termos de serviços',
'app.preview.down.block': 'Download this page to your local project',
...globalHeader,
...menu,
...settingDrawer,
...settings,
...pwa,
...component,
};

View File

@@ -1,5 +0,0 @@
export default {
'component.tagSelect.expand': 'Expandir',
'component.tagSelect.collapse': 'Diminuir',
'component.tagSelect.all': 'Todas',
};

View File

@@ -1,18 +0,0 @@
export default {
'component.globalHeader.search': 'Busca',
'component.globalHeader.search.example1': 'Exemplo de busca 1',
'component.globalHeader.search.example2': 'Exemplo de busca 2',
'component.globalHeader.search.example3': 'Exemplo de busca 3',
'component.globalHeader.help': 'Ajuda',
'component.globalHeader.notification': 'Notificação',
'component.globalHeader.notification.empty': 'Você visualizou todas as notificações.',
'component.globalHeader.message': 'Mensagem',
'component.globalHeader.message.empty': 'Você visualizou todas as mensagens.',
'component.globalHeader.event': 'Evento',
'component.globalHeader.event.empty': 'Você visualizou todos os eventos.',
'component.noticeIcon.clear': 'Limpar',
'component.noticeIcon.cleared': 'Limpo',
'component.noticeIcon.empty': 'Sem notificações',
'component.noticeIcon.loaded': 'Carregado',
'component.noticeIcon.view-more': 'Veja mais',
};

View File

@@ -1,52 +0,0 @@
export default {
'menu.welcome': 'Welcome',
'menu.more-blocks': 'More Blocks',
'menu.home': 'Início',
'menu.login': 'Login',
'menu.admin': 'Admin',
'menu.admin.sub-page': 'Sub-Page',
'menu.register': 'Registro',
'menu.register.result': 'Resultado de registro',
'menu.dashboard': 'Dashboard',
'menu.dashboard.analysis': 'Análise',
'menu.dashboard.monitor': 'Monitor',
'menu.dashboard.workplace': 'Ambiente de Trabalho',
'menu.exception.403': '403',
'menu.exception.404': '404',
'menu.exception.500': '500',
'menu.form': 'Formulário',
'menu.form.basic-form': 'Formulário Básico',
'menu.form.step-form': 'Formulário Assistido',
'menu.form.step-form.info': 'Formulário Assistido(gravar informações de transferência)',
'menu.form.step-form.confirm': 'Formulário Assistido(confirmar informações de transferência)',
'menu.form.step-form.result': 'Formulário Assistido(finalizado)',
'menu.form.advanced-form': 'Formulário Avançado',
'menu.list': 'Lista',
'menu.list.table-list': 'Tabela de Busca',
'menu.list.basic-list': 'Lista Básica',
'menu.list.card-list': 'Lista de Card',
'menu.list.search-list': 'Lista de Busca',
'menu.list.search-list.articles': 'Lista de Busca(artigos)',
'menu.list.search-list.projects': 'Lista de Busca(projetos)',
'menu.list.search-list.applications': 'Lista de Busca(aplicações)',
'menu.profile': 'Perfil',
'menu.profile.basic': 'Perfil Básico',
'menu.profile.advanced': 'Perfil Avançado',
'menu.result': 'Resultado',
'menu.result.success': 'Sucesso',
'menu.result.fail': 'Falha',
'menu.exception': 'Exceção',
'menu.exception.not-permission': '403',
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': 'Disparar',
'menu.account': 'Conta',
'menu.account.center': 'Central da Conta',
'menu.account.settings': 'Configurar Conta',
'menu.account.trigger': 'Disparar Erro',
'menu.account.logout': 'Sair',
'menu.editor': 'Graphic Editor',
'menu.editor.flow': 'Flow Editor',
'menu.editor.mind': 'Mind Editor',
'menu.editor.koni': 'Koni Editor',
};

View File

@@ -1,7 +0,0 @@
export default {
'app.pwa.offline': 'Você está offline agora',
'app.pwa.serviceworker.updated': 'Novo conteúdo está disponível',
'app.pwa.serviceworker.updated.hint':
'Por favor, pressione o botão "Atualizar" para recarregar a página atual',
'app.pwa.serviceworker.updated.ok': 'Atualizar',
};

View File

@@ -1,32 +0,0 @@
export default {
'app.setting.pagestyle': 'Configuração de estilo da página',
'app.setting.pagestyle.dark': 'Dark style',
'app.setting.pagestyle.light': 'Light style',
'app.setting.content-width': 'Largura do conteúdo',
'app.setting.content-width.fixed': 'Fixo',
'app.setting.content-width.fluid': 'Fluido',
'app.setting.themecolor': 'Cor do Tema',
'app.setting.themecolor.dust': 'Dust Red',
'app.setting.themecolor.volcano': 'Volcano',
'app.setting.themecolor.sunset': 'Sunset Orange',
'app.setting.themecolor.cyan': 'Cyan',
'app.setting.themecolor.green': 'Polar Green',
'app.setting.themecolor.daybreak': 'Daybreak Blue (default)',
'app.setting.themecolor.geekblue': 'Geek Glue',
'app.setting.themecolor.purple': 'Golden Purple',
'app.setting.navigationmode': 'Modo de Navegação',
'app.setting.sidemenu': 'Layout do Menu Lateral',
'app.setting.topmenu': 'Layout do Menu Superior',
'app.setting.fixedheader': 'Cabeçalho fixo',
'app.setting.fixedsidebar': 'Barra lateral fixa',
'app.setting.fixedsidebar.hint': 'Funciona no layout do menu lateral',
'app.setting.hideheader': 'Esconder o cabeçalho quando rolar',
'app.setting.hideheader.hint': 'Funciona quando o esconder cabeçalho está abilitado',
'app.setting.othersettings': 'Outras configurações',
'app.setting.weakmode': 'Weak Mode',
'app.setting.copy': 'Copiar Configuração',
'app.setting.copyinfo':
'copiado com sucessopor favor trocar o defaultSettings em src/models/setting.js',
'app.setting.production.hint':
'O painel de configuração apenas é exibido no ambiente de desenvolvimento, por favor modifique manualmente o',
};

View File

@@ -1,60 +0,0 @@
export default {
'app.settings.menuMap.basic': 'Configurações Básicas',
'app.settings.menuMap.security': 'Configurações de Segurança',
'app.settings.menuMap.binding': 'Vinculação de Conta',
'app.settings.menuMap.notification': 'Mensagens de Notificação',
'app.settings.basic.avatar': 'Avatar',
'app.settings.basic.change-avatar': 'Alterar avatar',
'app.settings.basic.email': 'Email',
'app.settings.basic.email-message': 'Por favor insira seu email!',
'app.settings.basic.nickname': 'Nome de usuário',
'app.settings.basic.nickname-message': 'Por favor insira seu nome de usuário!',
'app.settings.basic.profile': 'Perfil pessoal',
'app.settings.basic.profile-message': 'Por favor insira seu perfil pessoal!',
'app.settings.basic.profile-placeholder': 'Breve introdução sua',
'app.settings.basic.country': 'País/Região',
'app.settings.basic.country-message': 'Por favor insira país!',
'app.settings.basic.geographic': 'Província, estado ou cidade',
'app.settings.basic.geographic-message': 'Por favor insira suas informações geográficas!',
'app.settings.basic.address': 'Endereço',
'app.settings.basic.address-message': 'Por favor insira seu endereço!',
'app.settings.basic.phone': 'Número de telefone',
'app.settings.basic.phone-message': 'Por favor insira seu número de telefone!',
'app.settings.basic.update': 'Atualizar Informações',
'app.settings.security.strong': 'Forte',
'app.settings.security.medium': 'Média',
'app.settings.security.weak': 'Fraca',
'app.settings.security.password': 'Senha da Conta',
'app.settings.security.password-description': 'Força da senha',
'app.settings.security.phone': 'Telefone de Seguraça',
'app.settings.security.phone-description': 'Telefone vinculado',
'app.settings.security.question': 'Pergunta de Segurança',
'app.settings.security.question-description':
'A pergunta de segurança não está definida e a política de segurança pode proteger efetivamente a segurança da conta',
'app.settings.security.email': 'Email de Backup',
'app.settings.security.email-description': 'Email vinculado',
'app.settings.security.mfa': 'Dispositivo MFA',
'app.settings.security.mfa-description':
'O dispositivo MFA não vinculado, após a vinculação, pode ser confirmado duas vezes',
'app.settings.security.modify': 'Modificar',
'app.settings.security.set': 'Atribuir',
'app.settings.security.bind': 'Vincular',
'app.settings.binding.taobao': 'Vincular Taobao',
'app.settings.binding.taobao-description': 'Atualmente não vinculado à conta Taobao',
'app.settings.binding.alipay': 'Vincular Alipay',
'app.settings.binding.alipay-description': 'Atualmente não vinculado à conta Alipay',
'app.settings.binding.dingding': 'Vincular DingTalk',
'app.settings.binding.dingding-description': 'Atualmente não vinculado à conta DingTalk',
'app.settings.binding.bind': 'Vincular',
'app.settings.notification.password': 'Senha da Conta',
'app.settings.notification.password-description':
'Mensagens de outros usuários serão notificadas na forma de uma estação de letra',
'app.settings.notification.messages': 'Mensagens de Sistema',
'app.settings.notification.messages-description':
'Mensagens de sistema serão notificadas na forma de uma estação de letra',
'app.settings.notification.todo': 'Notificação de To-do',
'app.settings.notification.todo-description':
'A lista de to-do será notificada na forma de uma estação de letra',
'app.settings.open': 'Aberto',
'app.settings.close': 'Fechado',
};

View File

@@ -1,22 +0,0 @@
import component from './zh-CN/component';
import globalHeader from './zh-CN/globalHeader';
import menu from './zh-CN/menu';
import pwa from './zh-CN/pwa';
import settingDrawer from './zh-CN/settingDrawer';
import settings from './zh-CN/settings';
export default {
'navBar.lang': '语言',
'layout.user.link.help': '帮助',
'layout.user.link.privacy': '隐私',
'layout.user.link.terms': '条款',
'app.preview.down.block': '下载此页面到本地项目',
'app.welcome.link.fetch-blocks': '获取全部区块',
'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面',
...globalHeader,
...menu,
...settingDrawer,
...settings,
...pwa,
...component,
};

View File

@@ -0,0 +1,11 @@
import component from './zh-CN/component';
import menu from './zh-CN/menu';
import pages from './zh-CN/pages';
import pwa from './zh-CN/pwa';
export default {
...pages,
...menu,
...pwa,
...component,
};

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