mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-28 16:50:29 +00:00
Compare commits
152 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a10dccf5bd | ||
|
|
f40a41dbc1 | ||
|
|
4732bd943d | ||
|
|
89863b30e8 | ||
|
|
00ba0697dc | ||
|
|
30585f80e6 | ||
|
|
83ce5ec724 | ||
|
|
4467084c0e | ||
|
|
1f1f3af7b7 | ||
|
|
d38c08af56 | ||
|
|
2761e7afb9 | ||
|
|
8410f03eb0 | ||
|
|
e51c283c1d | ||
|
|
f694d1ad5d | ||
|
|
674408784b | ||
|
|
19e8aa2af6 | ||
|
|
3dd954541e | ||
|
|
ba614da809 | ||
|
|
b92e2a92fc | ||
|
|
718a4b1999 | ||
|
|
de83320306 | ||
|
|
4cfee561b9 | ||
|
|
6b78945c18 | ||
|
|
72d6981628 | ||
|
|
2243632d0c | ||
|
|
775991cfaa | ||
|
|
da097989f8 | ||
|
|
53164a4d2f | ||
|
|
3d3b3239d8 | ||
|
|
b1a97b08ec | ||
|
|
72663f68cb | ||
|
|
8c7db9b0e0 | ||
|
|
c72941f916 | ||
|
|
cc590d7c42 | ||
|
|
5e06e41477 | ||
|
|
6e658d1ea8 | ||
|
|
4832d95506 | ||
|
|
61b9816296 | ||
|
|
9a613da8c5 | ||
|
|
72ee5db872 | ||
|
|
cb68136f93 | ||
|
|
a181ff46b6 | ||
|
|
2e1433c366 | ||
|
|
bac35a425f | ||
|
|
08d0b4830a | ||
|
|
a1d9b3d032 | ||
|
|
8d0595ed4e | ||
|
|
01bd05d0a5 | ||
|
|
76d585795b | ||
|
|
4088a20252 | ||
|
|
810cbcaae4 | ||
|
|
dfdb833abe | ||
|
|
f10e53c1eb | ||
|
|
a88d0a5660 | ||
|
|
02bb9a1dc1 | ||
|
|
950238aa11 | ||
|
|
b52fc09fd6 | ||
|
|
062c906e95 | ||
|
|
69e860a1e8 | ||
|
|
0bd85d359a | ||
|
|
1abbe1f7cf | ||
|
|
88143ae64a | ||
|
|
b25e911a74 | ||
|
|
d8d891f6c9 | ||
|
|
a7f823940c | ||
|
|
3a47d5de73 | ||
|
|
eb2501f644 | ||
|
|
abce944aad | ||
|
|
386d5dd0ee | ||
|
|
509895511b | ||
|
|
c0ebe8dc62 | ||
|
|
ebf1b51d58 | ||
|
|
4b27f9b4e2 | ||
|
|
8f6d0fded8 | ||
|
|
e8cabdc13c | ||
|
|
a03d9f2626 | ||
|
|
37cb5ccb51 | ||
|
|
4c34048186 | ||
|
|
76e0bbcabd | ||
|
|
32bc74456a | ||
|
|
82df7c3a71 | ||
|
|
c06aca6f60 | ||
|
|
69dcf2fc5a | ||
|
|
04c9af5c81 | ||
|
|
1e6248d189 | ||
|
|
e3b4bcd2ca | ||
|
|
9730cb438d | ||
|
|
a8f48eb140 | ||
|
|
ab6a170c1d | ||
|
|
dc9b1eb6fc | ||
|
|
629bf00b63 | ||
|
|
1f2c38d3c7 | ||
|
|
b129637cf2 | ||
|
|
630f1e48bc | ||
|
|
4bed55b400 | ||
|
|
54ca5c115f | ||
|
|
ddba62fba0 | ||
|
|
15a24c65b9 | ||
|
|
56285ca540 | ||
|
|
b1c3f593db | ||
|
|
e23f6b3866 | ||
|
|
2943bc7ce1 | ||
|
|
da0f8916aa | ||
|
|
7609da212f | ||
|
|
7f87a47832 | ||
|
|
04868d217b | ||
|
|
56aa3c24d7 | ||
|
|
a54ba9bb54 | ||
|
|
fbe93dc847 | ||
|
|
dbd855cc25 | ||
|
|
7f136159c0 | ||
|
|
af61b59c5e | ||
|
|
c09e4df452 | ||
|
|
1694980eba | ||
|
|
feee1306fd | ||
|
|
0d56da4ab8 | ||
|
|
582c3788c4 | ||
|
|
b8fd6f9823 | ||
|
|
0fab691ba3 | ||
|
|
3cb172f7ff | ||
|
|
e882a650ae | ||
|
|
ef9c26ffec | ||
|
|
cf2a57c96a | ||
|
|
7acc52ebdb | ||
|
|
3c35d9dadc | ||
|
|
07742b6349 | ||
|
|
d45d086efb | ||
|
|
a31a316171 | ||
|
|
ec287a181f | ||
|
|
50f4d10b00 | ||
|
|
9f27f70da8 | ||
|
|
3df55a92f3 | ||
|
|
fd65c27ecb | ||
|
|
576d928a83 | ||
|
|
d14558a227 | ||
|
|
f48aba6f66 | ||
|
|
f51dccf956 | ||
|
|
a961b002c2 | ||
|
|
bffd43fa2c | ||
|
|
2a88f9221f | ||
|
|
efdef27d0a | ||
|
|
58bef9791f | ||
|
|
c1de71d122 | ||
|
|
f8e320b97c | ||
|
|
f69476df37 | ||
|
|
922892a909 | ||
|
|
f36f7296fd | ||
|
|
eff9d5f6e9 | ||
|
|
a75f32857a | ||
|
|
f011fe14bc | ||
|
|
7889a8c467 | ||
|
|
f37cb3a1a7 |
12
.github/ISSUE_TEMPLATE/question.md
vendored
12
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,12 +0,0 @@
|
||||
---
|
||||
name: Question
|
||||
about: Help wanted.
|
||||
title: "[Question]"
|
||||
labels: help wanted
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Please write your questions.
|
||||
-->
|
||||
10
.github/workflows/dashboard.yml
vendored
10
.github/workflows/dashboard.yml
vendored
@@ -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
|
||||
|
||||
|
||||
18
.github/workflows/go.yml
vendored
18
.github/workflows/go.yml
vendored
@@ -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
|
||||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -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
3
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
106
CONTRIBUTING.md
106
CONTRIBUTING.md
@@ -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
|
||||
|
||||
|
||||
36
Makefile
36
Makefile
@@ -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
8
OWNERS
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
20
README.md
20
README.md
@@ -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?
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
|
||||
|
||||
@@ -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{})
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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{})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,4 +7,4 @@ spec:
|
||||
workloadRefsPath: spec.workloadRefs
|
||||
allowComponentOverlap: true
|
||||
definitionRef:
|
||||
name: healthscope.core.oam.dev
|
||||
name: healthscopes.core.oam.dev
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
15
community.md
Normal 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.
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
388
config/samples/application/README.md
Normal file
388
config/samples/application/README.md
Normal 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
|
||||
```
|
||||
25
config/samples/application/application-sample.yaml
Normal file
25
config/samples/application/application-sample.yaml
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,4 +1,8 @@
|
||||
/lambda/
|
||||
/scripts
|
||||
/config
|
||||
.history
|
||||
.history
|
||||
public
|
||||
dist
|
||||
.umi
|
||||
mock
|
||||
@@ -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
37
dashboard/.gitignore
vendored
Normal 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
|
||||
@@ -20,3 +20,4 @@ yarn-error.log
|
||||
.history
|
||||
CNAME
|
||||
/build
|
||||
/public
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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: '/',
|
||||
},
|
||||
});
|
||||
68
dashboard/config/config.ts
Normal file
68
dashboard/config/config.ts
Normal 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'],
|
||||
},
|
||||
});
|
||||
@@ -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;
|
||||
22
dashboard/config/defaultSettings.ts
Normal file
22
dashboard/config/defaultSettings.ts
Normal 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;
|
||||
@@ -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
20
dashboard/config/proxy.ts
Normal 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,
|
||||
},
|
||||
},
|
||||
};
|
||||
52
dashboard/config/routes.ts
Normal file
52
dashboard/config/routes.ts
Normal 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',
|
||||
},
|
||||
];
|
||||
9
dashboard/config/themeSettings.ts
Normal file
9
dashboard/config/themeSettings.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
const themeSettings = {
|
||||
primaryColor: '#1b58f4', // 全局主色
|
||||
linkColor: '#1b58f4', // 链接色
|
||||
linkHoverColor: '#1b58f4',
|
||||
disabledBg: '#ebebeb', // 失效背景色,
|
||||
disabledColor: '#bebebe', // 失效文本色,
|
||||
btnDisableColor: '#a4a4a4', // 禁用btn文字颜色
|
||||
};
|
||||
export default themeSettings;
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
preview.pro.ant.design
|
||||
BIN
dashboard/public/favicon.ico
Normal file
BIN
dashboard/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
dashboard/public/icons/icon-128x128.png
Normal file
BIN
dashboard/public/icons/icon-128x128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
dashboard/public/icons/icon-192x192.png
Normal file
BIN
dashboard/public/icons/icon-192x192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
BIN
dashboard/public/icons/icon-512x512.png
Normal file
BIN
dashboard/public/icons/icon-512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
1
dashboard/public/logo.svg
Normal file
1
dashboard/public/logo.svg
Normal 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 |
5
dashboard/public/pro_icon.svg
Normal file
5
dashboard/public/pro_icon.svg
Normal 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
4
dashboard/src/access.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// src/access.ts
|
||||
export default function access() {
|
||||
return {};
|
||||
}
|
||||
79
dashboard/src/app.tsx
Normal file
79
dashboard/src/app.tsx
Normal 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,
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
5
dashboard/src/components/Footer/index.tsx
Normal file
5
dashboard/src/components/Footer/index.tsx
Normal 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={[]} />;
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
16
dashboard/src/components/HeaderDropdown/index.less
Normal file
16
dashboard/src/components/HeaderDropdown/index.less
Normal 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;
|
||||
}
|
||||
}
|
||||
21
dashboard/src/components/HeaderDropdown/index.tsx
Normal file
21
dashboard/src/components/HeaderDropdown/index.tsx
Normal 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;
|
||||
@@ -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;
|
||||
66
dashboard/src/components/RightContent/WorkSpaceDropDown.tsx
Normal file
66
dashboard/src/components/RightContent/WorkSpaceDropDown.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
dashboard/src/components/RightContent/index.tsx
Normal file
42
dashboard/src/components/RightContent/index.tsx
Normal 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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export default undefined;
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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,
|
||||
};
|
||||
11
dashboard/src/locales/en-US.ts
Normal file
11
dashboard/src/locales/en-US.ts
Normal 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,
|
||||
};
|
||||
@@ -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',
|
||||
};
|
||||
@@ -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',
|
||||
};
|
||||
9
dashboard/src/locales/en-US/menu.ts
Normal file
9
dashboard/src/locales/en-US/menu.ts
Normal 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',
|
||||
};
|
||||
6
dashboard/src/locales/en-US/pages.ts
Normal file
6
dashboard/src/locales/en-US/pages.ts
Normal 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.',
|
||||
};
|
||||
@@ -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 success,please replace defaultSettings in src/models/setting.js',
|
||||
'app.setting.production.hint':
|
||||
'Setting panel shows in development environment only, please manually modify',
|
||||
};
|
||||
@@ -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',
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
export default {
|
||||
'component.tagSelect.expand': 'Expandir',
|
||||
'component.tagSelect.collapse': 'Diminuir',
|
||||
'component.tagSelect.all': 'Todas',
|
||||
};
|
||||
@@ -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',
|
||||
};
|
||||
@@ -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',
|
||||
};
|
||||
@@ -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',
|
||||
};
|
||||
@@ -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 sucesso,por 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',
|
||||
};
|
||||
@@ -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',
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
11
dashboard/src/locales/zh-CN.ts
Normal file
11
dashboard/src/locales/zh-CN.ts
Normal 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
Reference in New Issue
Block a user