You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by su...@apache.org on 2020/11/21 11:11:16 UTC

[apisix-dashboard] branch master updated: release: release 2.0-rc version. (#598)

This is an automated email from the ASF dual-hosted git repository.

sunyi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git


The following commit(s) were added to refs/heads/master by this push:
     new b989a0f  release: release 2.0-rc version. (#598)
b989a0f is described below

commit b989a0f7d9630088991eadec844efeed39c0cf57
Author: litesun <7s...@gmail.com>
AuthorDate: Sat Nov 21 19:11:08 2020 +0800

    release: release 2.0-rc version. (#598)
    
    * Update CHANGELOG.md
    
    * Update CHANGELOG.zh-CN.md
    
    * Update CHANGELOG.md
    
    * Update CHANGELOG.zh-CN.md
    
    * Update CHANGELOG.md
    
    * feat: update deploy docs
    
    * feat: separate build and run to 2 scripts (#600)
    
    * feat: separate build and run
    
    * doc: update doc about build and run
    
    * doc: update doc about build and run
    
    * Update schema-sync.sh
    
    * Update develop.md
    
    * Update develop.zh-CN.md
    
    Co-authored-by: 琚致远 <ju...@apache.org>
    
    * feat: remove unused dist folder
    
    * doc: add etcd api version tips (#604)
    
    * doc: add etcd version tips(#602)
    
    * Update README.md
    
    * Update README.zh-CN.md
    
    Co-authored-by: 琚致远 <ju...@apache.org>
    
    * Update deploy.md
    
    * Update deploy.zh-CN.md
    
    * feat(docs): update deploy docs
    
    * feat(doc): added tip in deploy
    
    * feat(doc): update run part in deploy
    
    * feat(doc): added FAQ about how to redeploy
    
    * fix: 2.0 release issues (#606)
    
    * fix: remove json schema generation  from ci
    
    * fix: use json schema generated from APISIX v2.0
    
    * fix: ant-design/ant-design/issues/27396
    
    * fix: using relative path to read conf (#617)
    
    * fix: using  relative path
    
    * fix path
    
    * fix path
    
    * feat: support custom server host, port and DAG lib path (#625)
    
    * feat: support custom server port and dag lib path
    
    * test: custom host
    
    * test: add test case
    
    * fix bug
    
    * test
    
    * test
    
    * test
    
    * feat support config etcd endpoints
    
    * Update conf.json
    
    * Update conf.go
    
    * Update test-api.yml
    
    * Update deploy.md
    
    * Update deploy.zh-CN.md
    
    * Update deploy.md
    
    * Update conf.json
    
    Co-authored-by: 琚致远 <ju...@apache.org>
    
    * feat(doc): update config file
    
    * feat(doc): update Note
    
    * feat(docs): update NOTE
    
    * feat(doc): update ETCD endpoints
    
    * Update deploy.md
    
    * feat: refactor folders (#629)
    
    * feat: refactor api & frontend
    
    * feat: remove some actions
    
    * feat: added - v2.0
    
    * feat: added ignored files for checker
    
    * feat: trigger ci
    
    * feat: trigger CI
    
    * feat: added conf.json
    
    * fix: build dashboard
    
    * feat: remove demo temp
    
    * feat: update build.sh
    
    * chore: improve issue template and vscode (#660)
    
    * test: add e2e test for field hosts in `route` api (#612)
    
    * feat: ETCD cluster and APISIX cluster for CI
    
    * feat: add test cases
    
    * feat: add upstream service in docker compose
    
    * fix: comment
    
    * test: add test cases
    
    * fix: code format
    
    * test: add ci
    
    * fix ci
    
    * fix ci
    
    * fix ci
    
    * fix: remove consumer test
    
    * test: sleep for sync
    
    * test: e2e as an independent subproject
    
    * fix CI error
    
    * test: run docker
    
    * fix: remove json schema generate script in docker build
    
    * fix: check host and hosts config together
    
    * fix ci
    
    * test: add test cases for `host` in route
    
    * remove useless code
    
    * fix: using relative path to read conf (#617)
    
    * fix: using  relative path
    
    * fix path
    
    * fix path
    
    * fix conf path
    
    * fix ci error
    
    * fix etcd ip
    
    * fix: code format
    
    * run backend e2e test ci on v2 branch
    
    * fix: code format
    
    * fix: code format
    
    * fix: CI error
    
    * test: remove deploy CI again
    
    * remove useless codes
    
    * fix: go fmt
    
    * test: don't use `go fmt`
    
    * fix: code format
    
    * fix: var name
    
    * fix lint
    
    * fix CI error
    
    * debug
    
    * fix: docker container name
    
    * fix CI error
    
    * fix CI error
    
    * test: add more test cases
    
    * fix CI error
    
    * chore: remove useless code
    
    * fix: go fmt
    
    * fix: refactor test code
    
    * fix: check body
    
    * fix small issue
    
    * fix: update docker compose
    
    * fix CI
    
    * test
    
    * test
    
    * test: build image first
    
    * test: revert subnet ip in docker compose
    
    * fix by review
    
    * fix: docker compose
    
    * test: add test cases
    
    * fix: remove useless code
    
    * fix: code format
    
    * test: code format
    
    * feat: added e2e test for Login page (#619)
    
    * feat: added Front-end e2e test YAML file
    
    * feat: added login e2e test
    
    * feat: update Login.e2e.js
    
    * Update Login.e2e.js
    
    * feat: added e2e readme
    
    * feat: added licence
    
    * feat: added  start-server-and-test package
    
    * feat: update login test case
    
    * Update frontend-e2e-test.yml
    
    * feat: added logout test case
    
    * Update frontend-e2e-test.yml
    
    * feat: added login failed with empty input
    
    * feat: update CI
    
    * feat: update text
    
    * feat: added public.js
    
    * feat: change logout timeout
    
    * feat: Added e2e test documentation link to development.md
    
    * Update develop.md
    
    * Update develop.zh-CN.md
    
    * Update README.md
    
    Co-authored-by: 琚致远 <ju...@apache.org>
    
    * CI: collect golang unit test code coverage (#654)
    
    * fix: promethus incorrect value when update route (#666)
    
    * feat: deploy with Docker (#657)
    
    * feat: added Docker
    
    * feat: added License header
    
    * feat: added line in docs
    
    * Update test-docker.yml
    
    * feat(doc): update docs
    
    * feat: added extra line
    
    * feat: added Go Proxy in Dockerfile
    
    * feat: update Dockerfile
    
    * chore: update docs
    
    * fix: copy correct files
    
    * feat: improve Dockerfile
    
    * Revert "feat: improve Dockerfile"
    
    This reverts commit c68a4c4e479147e65efae4611ba162bd2c465928.
    
    * fix run fail
    
    * feat: update docs
    
    * fix: compatible with Golang conf
    
    * Squashed commit of the following:
    
    commit 94450bf6ad52cdcac8d527c7e7fbf9e8fa1c4ab3
    Author: litesun <7s...@gmail.com>
    Date:   Tue Nov 3 10:19:04 2020 +0800
    
        fix: promethus incorrect value when update route (#666)
    
    commit de8bdbf6ff867a05dffbd05151b1ba07603f46d2
    Author: nic-chen <33...@users.noreply.github.com>
    Date:   Tue Nov 3 07:50:29 2020 +0800
    
        CI: collect golang unit test code coverage (#654)
    
    commit 722c0fdeddbc34723f730e36a4cff004889f7093
    Author: litesun <7s...@gmail.com>
    Date:   Tue Nov 3 00:03:06 2020 +0800
    
        feat: added e2e test for Login page (#619)
    
        * feat: added Front-end e2e test YAML file
    
        * feat: added login e2e test
    
        * feat: update Login.e2e.js
    
        * Update Login.e2e.js
    
        * feat: added e2e readme
    
        * feat: added licence
    
        * feat: added  start-server-and-test package
    
        * feat: update login test case
    
        * Update frontend-e2e-test.yml
    
        * feat: added logout test case
    
        * Update frontend-e2e-test.yml
    
        * feat: added login failed with empty input
    
        * feat: update CI
    
        * feat: update text
    
        * feat: added public.js
    
        * feat: change logout timeout
    
        * feat: Added e2e test documentation link to development.md
    
        * Update develop.md
    
        * Update develop.zh-CN.md
    
        * Update README.md
    
        Co-authored-by: 琚致远 <ju...@apache.org>
    
    commit 915ce8300b8138eb986737e6df1201fff63d3b57
    Author: nic-chen <33...@users.noreply.github.com>
    Date:   Mon Nov 2 21:13:34 2020 +0800
    
        test: add e2e test for field hosts in `route` api (#612)
    
        * feat: ETCD cluster and APISIX cluster for CI
    
        * feat: add test cases
    
        * feat: add upstream service in docker compose
    
        * fix: comment
    
        * test: add test cases
    
        * fix: code format
    
        * test: add ci
    
        * fix ci
    
        * fix ci
    
        * fix ci
    
        * fix: remove consumer test
    
        * test: sleep for sync
    
        * test: e2e as an independent subproject
    
        * fix CI error
    
        * test: run docker
    
        * fix: remove json schema generate script in docker build
    
        * fix: check host and hosts config together
    
        * fix ci
    
        * test: add test cases for `host` in route
    
        * remove useless code
    
        * fix: using relative path to read conf (#617)
    
        * fix: using  relative path
    
        * fix path
    
        * fix path
    
        * fix conf path
    
        * fix ci error
    
        * fix etcd ip
    
        * fix: code format
    
        * run backend e2e test ci on v2 branch
    
        * fix: code format
    
        * fix: code format
    
        * fix: CI error
    
        * test: remove deploy CI again
    
        * remove useless codes
    
        * fix: go fmt
    
        * test: don't use `go fmt`
    
        * fix: code format
    
        * fix: var name
    
        * fix lint
    
        * fix CI error
    
        * debug
    
        * fix: docker container name
    
        * fix CI error
    
        * fix CI error
    
        * test: add more test cases
    
        * fix CI error
    
        * chore: remove useless code
    
        * fix: go fmt
    
        * fix: refactor test code
    
        * fix: check body
    
        * fix small issue
    
        * fix: update docker compose
    
        * fix CI
    
        * test
    
        * test
    
        * test: build image first
    
        * test: revert subnet ip in docker compose
    
        * fix by review
    
        * fix: docker compose
    
        * test: add test cases
    
        * fix: remove useless code
    
        * fix: code format
    
        * test: code format
    
    commit 94d024544df0caff6aef69f6ca9792954a285304
    Author: 琚致远 <ju...@apache.org>
    Date:   Mon Nov 2 13:17:42 2020 +0800
    
        chore: improve issue template and vscode (#660)
    
    * Revert "Squashed commit of the following:"
    
    This reverts commit 93d38eef07058f5c65ac1eb83aac9d04ec12d24d.
    
    * feat: remove output from ignore files
    
    Co-authored-by: nic-chen <jo...@163.com>
    
    * feat: use web instead of frontend (#674)
    
    * feat: use web instead of frontend
    
    * feat: rename frontend to web
    
    * feat: remove all frontend to web
    
    * feat: support get plugin schema based on schema_type (#651)
    
    * feat: support get plugin schema based on schema_type
    
    * fix: ci errors
    
    * fix: run error casued by api-breaker.lua
    
    * fix: get schema_type from query and add some test
    
    * fix: update schema.json file
    
    * fix: update validate to support schematype
    
    * fix: properties:{} validate failed
    
    * fix: some code errors refer to the review
    
    * test: add linter for manager api (#655)
    
    * test: add go lint
    
    * fix lint
    
    * fix by review
    
    * fix errors
    
    * test: run lint on v2.0
    
    * fix: text format
    
    * fix: os check
    
    * fix typo
    
    * fix: fmt --> log
    
    * fix log
    
    * fix: unnecessary nil check around range for lint
    
    * fix: trigger lint for push to branch v2.0
    
    * feat: update plugin to 1.0.10
    
    * fix: enable HTTPS setting unsuccessful in Route (#692)
    
    * fix: wrong stepHeader after edit MatchingRules
    
    * feat: disable plugin orchestration when select forcehttps
    
    * feat: update Route transform
    
    * feat: cache Front-end e2e CI node_modules (#696)
    
    * chore: refactor `conf` of `manager api` (#693)
    
    * feat: refactor conf
    
    * fix default listen port
    
    * fix build and run scripts
    
    * fix: docs
    
    * chore: remove useless file
    
    * fix docker for test
    
    * fix CI
    
    * fix CI
    
    * fix ci
    
    * fix: `-c` conf dir  -->  `-p` work dir
    
    * fix go test error
    
    * fix conf
    
    * fix: revert changes
    
    * fix: remove useless comment
    
    * fix: remove useless comment
    
    * doc: add comment for config
    
    * doc: todo
    
    * fix: config format
    
    * fix: if secret use default value, should generate a random string to replace it.
    
    * fix comment style
    
    * fix: change web dir in docker file
    
    * doc: update comments
    
    * fix:bug that dirty data exists after updating route and wrong mod for prod env (#704)
    
    * fix: route bug, dirty data exists after updating
    
    * fix mod for env
    
    * fix mod for env
    
    * fix error log
    
    * fix error log
    
    * chore: refactor error log for `manager api` (#689)
    
    * chore: refactor log
    
    * fix: custom log by conf
    
    * feat: add error log
    
    * fix default config
    
    * fix CI fail
    
    * fix: should not save log to file by default
    
    * test: add test case
    
    * test: add test case
    
    * fix CI fail
    
    * fix error
    
    * fix CI
    
    * fix error
    
    * fix according to reviews
    
    * test: more test cases
    
    * fix error
    
    * chore: use `/dev/stdout` as default log file path
    
    * fix typo
    
    * fix docker for logs dir
    
    * fix CI fail
    
    * fix: delete useless files
    
    * fix: change file name
    
    * bugfix: dashboard 2.0 failed to fetch ssl certificate not found  (#719)
    
    * fix: set ssl status, since it's default 0
    
    * add test cases
    
    * test: add test cases
    
    * fix: update test cases
    
    * fix: test case fail
    
    * test: remove hosts in hosts setting
    
    * fix: according review
    
    * fix: according review
    
    * chore: add comment
    
    * fix: update order (#744)
    
    * feat(Consumer): use username instead of id (#742)
    
    * feat(Consumer): use username instead of id
    
    * feat: remove duplicated var
    
    * chore: exit if any error and specify the download file name when download by `wget`. (#751)
    
    fix #646
    
    * fix: an error will occur if `pass_host` is set to `node` when creating upstream (#750)
    
    * fix: bug #749
    
    * fix: bug #749
    
    * test: add test cases
    
    * fix: CI
    
    * fix: CI fail
    
    * test: add e2e test cases for upstream (#738)
    
    * fix: react warnings (#747)
    
    * fix: update json schema (#754)
    
    * chore: move the Dockerfile to `test` folder, because it was used for testing (#753)
    
    * feat: convert uri to uris (#740)
    
    * feat: deploy with docker (#701)
    
    * feat: added deploy with docker CD
    
    * feat: docker deploy test
    
    * fix: CI
    
    * fix: path of `config.yaml`
    
    * fix: CI fail
    
    * docs: update doc for docker deploy
    
    * fix: typo
    
    * fix: add EOF && trigger  CD
    
    Co-authored-by: nic-chen <jo...@163.com>
    Co-authored-by: kv <gx...@163.com>
    
    * fix: invalid values from the manager-api (#736)
    
    * feat: return None when timestamp is invalid
    
    * chore: added TODO
    
    * docs: add makefile && modify  develop and deploy  docs (#729)
    
    * docs: add makefile && modify  develop and deploy  docs
    
    * docs: Make the makefile clearer
    
    * docs: modify frontend to web
    
    * doc: two blankline between commands
    
    * doc: remove blanklien EOF
    
    * docs: remove make run/stop in makefile
    
    * docs: make readme readable
    
    * docs: declear in readme
    
    * docs: make readme readable
    
    * docs: remove dependencies in readme
    
    * docs: check english desc
    
    * docs: style adjust
    
    * docs: sync makefile desc
    
    * docs: remove blank
    
    * docs: style adjust
    
    * docs: remove ENV=local in deploy
    
    * fix: modify lint ci
    
    * docs: unify go-lint
    
    * docs: check all text
    
    * docs: makefile Aligned
    
    * docs: update style
    
    * docs: add markdown code style
    
    * docs: fix  syntax
    
    * docs: remove Self-referencing
    
    * docs: user guide
    
    * docs: use header
    
    * docs: style fix
    
    * docs: add desc
    
    * docs: modify 'pack' to 'package'
    
    * docs: unify manager-api
    
    * docs: mv Install to Installation
    
    * docs: node to Node.js
    
    * docs: source codes to Source Codes
    
    * docs: Source Codes
    
    * fix: mkdir -p ./output/logs in makefile build
    
    * feat: support specifying APISIX path to generate json schema (#765)
    
    * feat: support specifying APISIX path to generate json schema
    
    * doc: update doc about schema sync
    
    * fix: doc
    
    * fix: errors
    
    * fix: error according to test
    
    * test
    
    * no message
    
    * test
    
    * fix: remove debug
    
    * test: add consumer e2e test (#735)
    
    * add consumer e2e test
    add public method "PartialBody"
    
    * test: add end with EOL
    
    * add test data plane to case 2
    delete some useless code
    
    * modify code style
    
    * fix: makefile build error (#767)
    
    * fix: used dashboard add consumer of jwt, would have an error when get the jwt token (#768)
    
    * feat: update changelog (#771)
    
    * Update CHANGELOG.md
    
    * Update CHANGELOG.zh-CN.md
    
    * docs: update change log
    
    Co-authored-by: nic-chen <jo...@163.com>
    
    * fix: consumer schema for auth plugin (#770)
    
    * fix: consumer schema for auth plugin
    
    * fix: update by jsonschema
    
    * fix: json schema
    
    * fix: doc (#772)
    
    * fix: doc (#774)
    
    * feat: remove CD for PR
    
    * fix: closed WatchResponse channel when cancel function is called (#779) (#795)
    
    * ci: fix CI naming (#799)
    
    * fix: make cli test more compatible (#798)
    
    * test: fix e2e test unstable (#800)
    
    * fix: double scroll bar in plugin page (#801)
    
    * feat(docs): improve README & Deploy (#785)
    
    * feat(doc): update README & deploy
    
    * feat(docs): added FAQ
    
    * fix: linkx
    
    * feat(docs): update deploy with docker
    
    * feat(docs): update FAQ
    
    * feat: update README
    
    * feat: update README
    
    * feta: update README
    
    * fix: link
    
    * feat: update deploy with docker
    
    * feat(docs): added tip for some users
    
    * feat(doc): improve deploy with docker
    
    * feat(docs): improve deploy with docker
    
    * feat(doc): update run with docker
    
    * feat(doc): improve deploy
    
    * feat(doc): added more info
    
    * feat(doc): added more info
    
    * feat(doc): remove extra info
    
    * feat: added more detailed
    
    * fix: CI fail according `api/conf/conf.yaml ` is  changed.
    
    * fix: revert changed
    
    * feat(docs): improve English version
    
    * feat(docs): use frontend instead, just like backend
    
    * feat(docs): remove extra statements
    
    * feat(docs): update typo
    
    * feat(docs): remove startup with message
    
    * feat: update CI's name
    
    Co-authored-by: nic-chen <jo...@163.com>
    
    * feat: skip puppeteer chromium download when build (#808)
    
    * feat(Makefile): update release-src (#816)
    
    * ci: fix CI fail (#818)
    
    * ci: fix CI fail
    
    * ci: fix CI fail
    
    * ci: fix CI fail
    
    * test: add e2e test for config route with service_id or upstream_id (#810)
    
    * test: add e2e test for config route with service_id or upstream_id
    
    * test: fix route test
    
    * test: fix route test
    
    * test: fix route test
    
    * test: fix route test format
    
    * test: fix route test format
    
    * test: fix test format
    
    * test: add test on data plane
    
    * test: fix test conflict
    
    * feat: install signal handler for graceful shutdown (#737) (#796)
    
    * Revert "test: add consumer e2e test (#735)" (#829)
    
    This reverts commit c140f41acdc90b53db3b349f63a43503666a76fe.
    
    * feat: add a hanlder unit test for upstream and remove init
    
    * append license
    
    * revert unreviewed pr (#841)
    
    Co-authored-by: nic-chen <33...@users.noreply.github.com>
    Co-authored-by: 琚致远 <ju...@apache.org>
    Co-authored-by: jiayx <ji...@users.noreply.github.com>
    Co-authored-by: nic-chen <jo...@163.com>
    Co-authored-by: liuxiran <be...@126.com>
    Co-authored-by: YuanSheng Wang <me...@gmail.com>
    Co-authored-by: kv <gx...@163.com>
    Co-authored-by: idbeta <id...@gmail.com>
    Co-authored-by: Peter Zhu <st...@gmail.com>
    Co-authored-by: EnableAsync <43...@users.noreply.github.com>
    Co-authored-by: ShiningRush <27...@qq.com>
---
 .actions/ASF-Release.cfg                           |   20 +-
 .asf.yaml                                          |    1 -
 .dockerignore                                      |    2 +-
 .github/ISSUE_TEMPLATE                             |   23 -
 .github/ISSUE_TEMPLATE/bug-report.md               |   36 +
 .github/ISSUE_TEMPLATE/config.yml                  |    5 +
 .github/ISSUE_TEMPLATE/feature-request.md          |   23 +
 .github/workflows/backend-cli-test.yml             |   31 +
 .github/workflows/backend-e2e-test.yml             |   33 +
 .github/workflows/backend-unit-test.yml            |   74 +
 .github/workflows/deploy-api.yml                   |   70 -
 .github/workflows/deploy-frontend.yml              |   22 -
 .github/workflows/deploy-with-docker.yml           |   44 +
 .github/workflows/frontend-e2e-test.yml            |   40 +
 .../workflows/{license-checker.yml => go-lint.yml} |   12 +-
 .github/workflows/license-checker.yml              |    1 +
 .github/workflows/test-api.yml                     |   61 -
 .github/workflows/test-deploy-with-go.yml          |   73 -
 .../test-frontend-multiple-node-build.yml          |   12 +-
 .gitignore                                         |   11 +-
 .vscode/settings.json                              |    8 +
 CHANGELOG.md                                       |   24 +
 CHANGELOG.zh-CN.md                                 |   26 +
 Dockerfile                                         |   75 +-
 Makefile                                           |   98 +-
 README.md                                          |   59 +-
 README.zh-CN.md                                    |   66 +-
 api/build-tools/schema-sync.lua                    |   15 +-
 api/{entry.sh => build-tools/schema-sync.sh}       |   39 +-
 compose/manager_conf/entry.sh => api/build.sh      |   31 +-
 api/conf/conf.go                                   |  171 +-
 api/conf/conf.json                                 |   23 -
 api/conf/conf.yaml                                 |   41 +
 api/conf/conf_preview.json                         |   23 -
 api/conf/schema.json                               | 3500 +++++++++++++++++++-
 api/docker-compose.yml                             |   25 -
 api/entry.sh                                       |   18 +-
 api/filter/authentication.go                       |    2 +-
 api/filter/logging.go                              |   70 +-
 api/filter/recover.go                              |   15 +-
 api/go.mod                                         |    4 +
 api/go.sum                                         |  234 +-
 api/internal/core/entity/entity.go                 |   45 +-
 api/internal/core/entity/format.go                 |   19 +-
 api/internal/core/entity/format_test.go            |    4 +-
 api/internal/core/storage/etcd.go                  |    5 +-
 api/internal/core/store/query.go                   |    4 +-
 api/internal/core/store/store.go                   |   17 +-
 api/internal/core/store/store_test.go              |    6 +-
 api/internal/core/store/storehub.go                |    3 +
 api/internal/core/store/validate.go                |   79 +-
 api/internal/core/store/validate_test.go           |  175 +-
 .../handler/authentication/authentication.go       |    4 +-
 .../handler/authentication/authentication_test.go  |   17 +-
 api/internal/handler/consumer/consumer.go          |   14 +
 api/internal/handler/consumer/consumer_test.go     |   90 +-
 api/internal/handler/plugin/plugin.go              |   16 +-
 api/internal/handler/plugin/plugin_test.go         |   54 +-
 api/internal/handler/route/route.go                |   24 +-
 api/internal/handler/route/route_test.go           | 1652 ++++-----
 api/internal/handler/service/service_test.go       |   66 +-
 api/internal/handler/ssl/ssl.go                    |    6 +
 api/internal/handler/ssl/ssl_test.go               |   18 +-
 api/internal/handler/upstream/upstream_test.go     |  250 +-
 api/internal/route.go                              |    2 +-
 api/log/log.go                                     |  207 +-
 api/log/zap.go                                     |   89 +
 api/{dist/.gitkeep => logs/placeholder.txt}        |    0
 api/main.go                                        |   41 +-
 api/run.sh                                         |   41 +-
 api/test/certs/apisix.crt                          |   27 +
 api/test/certs/apisix.key                          |   39 +
 api/test/certs/test2.crt                           |   28 +
 api/test/certs/test2.key                           |   39 +
 api/test/docker-deploy/docker-compose.yaml         |   66 +
 api/{ => test/docker}/Dockerfile                   |   22 +-
 .../test/docker/apisix_config.yaml                 |   31 +-
 api/test/docker/docker-compose.yaml                |  183 +
 api/test/docker/manager-api-conf.yaml              |   43 +
 api/test/docker/upstream.conf                      |   72 +
 api/test/e2e/base.go                               |  170 +
 api/test/e2e/go.mod                                |   10 +
 api/test/e2e/go.sum                                |  126 +
 api/test/e2e/route_service_upstream_test.go        |  304 ++
 api/test/e2e/route_test.go                         |  249 ++
 api/test/e2e/ssl_test.go                           |  146 +
 api/test/e2e/upstream_test.go                      |  243 ++
 api/test/shell/cli_test.sh                         |   95 +
 api/test/shell/docker_deploy_test.sh               |   43 +
 compose/dashboard_conf/nginx.conf                  |   39 -
 compose/docker-compose.yml                         |  134 -
 compose/grafana_conf/config/grafana.ini            |  756 -----
 .../dashboards/apisix_http_prometheus.json         |  933 ------
 .../grafana_conf/provisioning/dashboards/all.yaml  |   27 -
 .../grafana_conf/provisioning/datasources/all.yaml |   25 -
 compose/prometheus_conf/prometheus.yml             |   39 -
 docker/nginx.conf                                  |   38 -
 docs/FAQ.md                                        |   56 +
 docs/FAQ.zh-CN.md                                  |   56 +
 docs/USER_GUIDE.md                                 |    2 -
 docs/USER_GUIDE.zh-CN.md                           |    2 +-
 docs/deploy-with-docker.md                         |   81 +
 docs/deploy-with-docker.zh-CN.md                   |   81 +
 docs/deploy.md                                     |   79 +-
 docs/deploy.zh-CN.md                               |   73 +-
 docs/develop.md                                    |   57 +-
 docs/develop.zh-CN.md                              |   54 +-
 docs/images/architecture.png                       |  Bin 0 -> 162681 bytes
 src/pages/Consumer/index.ts                        |   16 -
 .editorconfig => web/.editorconfig                 |    0
 .eslintignore => web/.eslintignore                 |    0
 .eslintrc.js => web/.eslintrc.js                   |    0
 .prettierignore => web/.prettierignore             |    0
 .prettierrc.js => web/.prettierrc.js               |    0
 .stylelintrc.js => web/.stylelintrc.js             |    0
 {config => web/config}/config.ts                   |    1 +
 {config => web/config}/defaultSettings.ts          |    0
 {config => web/config}/proxy.ts                    |    0
 {config => web/config}/routes.ts                   |    2 +-
 jest.config.js => web/jest.config.js               |    0
 jsconfig.json => web/jsconfig.json                 |    0
 {mock => web/mock}/notices.ts                      |    0
 {mock => web/mock}/route.ts                        |    0
 {mock => web/mock}/user.ts                         |    0
 package.json => web/package.json                   |    5 +-
 {public => web/public}/empty.svg                   |    0
 {public => web/public}/favicon.png                 |  Bin
 {scripts => web/scripts}/verifyCommit.js           |    0
 {src => web/src}/access.ts                         |    0
 {src => web/src}/app.tsx                           |    0
 {src => web/src}/assets/logo.svg                   |    0
 .../src}/components/ActionBar/ActionBar.tsx        |    0
 {src => web/src}/components/ActionBar/index.ts     |    0
 .../src}/components/ActionBar/locales/en-US.ts     |    0
 .../src}/components/ActionBar/locales/zh-CN.ts     |    0
 {src => web/src}/components/Footer/index.tsx       |    0
 .../src}/components/HeaderDropdown/index.less      |    0
 .../src}/components/HeaderDropdown/index.tsx       |    0
 .../src}/components/NoticeIcon/NoticeList.less     |    0
 .../src}/components/NoticeIcon/NoticeList.tsx      |    0
 {src => web/src}/components/NoticeIcon/index.less  |    0
 {src => web/src}/components/NoticeIcon/index.tsx   |    0
 {src => web/src}/components/PageLoading/index.tsx  |    0
 .../components/RightContent/AvatarDropdown.tsx     |    0
 .../src}/components/RightContent/index.less        |    0
 {src => web/src}/components/RightContent/index.tsx |    0
 .../src}/components/Upstream/UpstreamForm.tsx      |   47 +-
 {src => web/src}/components/Upstream/constant.ts   |    0
 {src => web/src}/components/Upstream/index.ts      |    0
 {src => web/src}/constants.ts                      |    0
 web/src/e2e/Login.e2e.js                           |   75 +
 web/src/e2e/Logout.e2e.js                          |   56 +
 docs/develop.md => web/src/e2e/README.md           |   24 +-
 compose/README.md => web/src/e2e/README.zh-CN.md   |   22 +-
 .../src}/e2e/__mocks__/antd-pro-merge-less.js      |    0
 {src => web/src}/e2e/baseLayout.e2e.js             |    0
 .../Preview.tsx => web/src/e2e/service.js          |   32 +-
 {src => web/src}/global.less                       |    5 +
 {src => web/src}/global.tsx                        |    0
 {src => web/src}/helpers.tsx                       |   10 +
 {src => web/src}/iconfont.ts                       |    0
 {src => web/src}/locales/en-US.ts                  |    0
 {src => web/src}/locales/en-US/component.ts        |    0
 {src => web/src}/locales/en-US/globalHeader.ts     |    0
 {src => web/src}/locales/en-US/menu.ts             |    0
 {src => web/src}/locales/en-US/pwa.ts              |    0
 .../zh-CN => web/src/locales/en-US}/setting.ts     |    0
 {src => web/src}/locales/en-US/settingDrawer.ts    |    0
 {src => web/src}/locales/zh-CN.ts                  |    0
 {src => web/src}/locales/zh-CN/component.ts        |    0
 {src => web/src}/locales/zh-CN/globalHeader.ts     |    0
 {src => web/src}/locales/zh-CN/menu.ts             |    0
 {src => web/src}/locales/zh-CN/pwa.ts              |    0
 .../en-US => web/src/locales/zh-CN}/setting.ts     |    0
 {src => web/src}/locales/zh-CN/settingDrawer.ts    |    0
 {src => web/src}/manifest.json                     |    0
 {src => web/src}/pages/404.tsx                     |    0
 {src => web/src}/pages/Consumer/Create.tsx         |   18 +-
 {src => web/src}/pages/Consumer/List.tsx           |    8 +-
 .../src}/pages/Consumer/components/Preview.tsx     |    0
 .../src}/pages/Consumer/components/Step1.tsx       |    0
 {src => web/src}/pages/Consumer/locales/en-US.ts   |    0
 {src => web/src}/pages/Consumer/locales/zh-CN.ts   |    0
 {src => web/src}/pages/Consumer/service.ts         |   10 +-
 {src => web/src}/pages/Consumer/typing.d.ts        |    0
 {src => web/src}/pages/Metrics/Metrics.tsx         |    0
 {src => web/src}/pages/Metrics/index.ts            |    0
 {src => web/src}/pages/Metrics/locales/en-US.ts    |    0
 {src => web/src}/pages/Metrics/locales/zh-CN.ts    |    0
 {src => web/src}/pages/Metrics/service.ts          |    0
 {src => web/src}/pages/Route/Create.less           |    0
 {src => web/src}/pages/Route/Create.tsx            |   20 +-
 {src => web/src}/pages/Route/List.tsx              |   32 +-
 .../Route/components/CreateStep4/CreateStep4.tsx   |    0
 .../pages/Route/components/CreateStep4/index.ts    |    0
 .../Route/components/ResultView/ResultView.tsx     |    0
 .../pages/Route/components/ResultView/index.ts     |    0
 .../Route/components/Step1/MatchingRulesView.tsx   |    2 +-
 .../src}/pages/Route/components/Step1/MetaView.tsx |    0
 .../Route/components/Step1/RequestConfigView.tsx   |    0
 .../src}/pages/Route/components/Step1/index.tsx    |    0
 .../Route/components/Step2/RequestRewriteView.tsx  |    0
 .../src}/pages/Route/components/Step2/index.tsx    |    0
 .../src}/pages/Route/components/Step3/index.tsx    |   57 +-
 {src => web/src}/pages/Route/constants.ts          |    0
 {src => web/src}/pages/Route/index.ts              |    0
 {src => web/src}/pages/Route/locales/en-US.ts      |    0
 {src => web/src}/pages/Route/locales/zh-CN.ts      |    0
 {src => web/src}/pages/Route/service.ts            |    0
 {src => web/src}/pages/Route/transform.ts          |   53 +-
 {src => web/src}/pages/Route/typing.d.ts           |    4 +
 {src => web/src}/pages/SSL/Create.less             |    0
 {src => web/src}/pages/SSL/Create.tsx              |    0
 {src => web/src}/pages/SSL/List.tsx                |    6 +-
 .../pages/SSL/components/CertificateForm/index.tsx |    0
 .../SSL/components/CertificateUploader/index.tsx   |    0
 .../src}/pages/SSL/components/Step1/index.tsx      |    0
 .../src}/pages/SSL/components/Step2/index.tsx      |    0
 {src => web/src}/pages/SSL/locales/en-US.ts        |    0
 {src => web/src}/pages/SSL/locales/zh-CN.ts        |    0
 {src => web/src}/pages/SSL/service.ts              |    0
 {src => web/src}/pages/SSL/style.less              |    0
 {src => web/src}/pages/SSL/typing.d.ts             |    0
 {src => web/src}/pages/Setting/Setting.tsx         |    0
 {src => web/src}/pages/Setting/index.ts            |    0
 {src => web/src}/pages/Setting/locales/en-US.ts    |    0
 {src => web/src}/pages/Setting/locales/zh-CN.ts    |    0
 {src => web/src}/pages/Setting/service.ts          |    0
 {src => web/src}/pages/Setting/style.less          |    0
 {src => web/src}/pages/Setting/typingd.d.ts        |    0
 {src => web/src}/pages/Upstream/Create.tsx         |    0
 {src => web/src}/pages/Upstream/List.tsx           |    4 +-
 .../src}/pages/Upstream/components/Step1.tsx       |    0
 {src => web/src}/pages/Upstream/constants.ts       |    0
 {src => web/src}/pages/Upstream/index.ts           |    0
 {src => web/src}/pages/Upstream/locales/en-US.ts   |    0
 {src => web/src}/pages/Upstream/locales/zh-CN.ts   |    0
 {src => web/src}/pages/Upstream/service.ts         |    0
 {src => web/src}/pages/Upstream/transform.ts       |    6 +-
 {src => web/src}/pages/Upstream/typing.d.ts        |    4 +-
 {src => web/src}/pages/User/Login.less             |    0
 {src => web/src}/pages/User/Login.tsx              |    0
 {src => web/src}/pages/User/Logout.tsx             |    0
 .../pages/User/components/LoginMethodExample.tsx   |    0
 .../pages/User/components/LoginMethodPassword.tsx  |    0
 {src => web/src}/pages/User/index.ts               |    0
 {src => web/src}/pages/User/locales/en-US.ts       |    0
 {src => web/src}/pages/User/locales/zh-CN.ts       |    0
 {src => web/src}/pages/User/typing.d.ts            |    0
 {src => web/src}/pages/document.ejs                |    0
 {src => web/src}/service-worker.js                 |    0
 {src => web/src}/services/API.d.ts                 |    0
 {src => web/src}/services/user.ts                  |    0
 {src => web/src}/typings.d.ts                      |    0
 {tests => web/tests}/PuppeteerEnvironment.js       |    0
 {tests => web/tests}/beforeTest.js                 |    0
 {tests => web/tests}/getBrowser.js                 |    0
 {tests => web/tests}/run-tests.js                  |    0
 tsconfig.json => web/tsconfig.json                 |    0
 yarn.lock => web/yarn.lock                         |  227 +-
 260 files changed, 8904 insertions(+), 4419 deletions(-)

diff --git a/.actions/ASF-Release.cfg b/.actions/ASF-Release.cfg
index 1ac4118..bed65bf 100644
--- a/.actions/ASF-Release.cfg
+++ b/.actions/ASF-Release.cfg
@@ -68,16 +68,16 @@ ASFLicenseHeaderLua.txt
 .tox
 
 # Skip files containing MIT License
-scripts/verifyCommit.js
-src/components/HeaderDropdown/index.less
-src/components/HeaderDropdown/index.tsx
-src/components/NoticeIcon
-src/components/PageLoading/index.tsx
-src/components/RightContent
-src/e2e/__mocks__/antd-pro-merge-less.js
-src/e2e/baseLayout.e2e.js
-src/pages/404.tsx
-src/service-worker.js
+web/scripts/verifyCommit.js
+web/src/components/HeaderDropdown/index.less
+web/src/components/HeaderDropdown/index.tsx
+web/src/components/NoticeIcon
+web/src/components/PageLoading/index.tsx
+web/src/components/RightContent
+web/src/e2e/__mocks__/antd-pro-merge-less.js
+web/src/e2e/baseLayout.e2e.js
+web/src/pages/404.tsx
+web/src/service-worker.js
 api/build-tools/json.lua
 
 # Skip files containing Apache 2.0 License
diff --git a/.asf.yaml b/.asf.yaml
index b2617d9..59b805a 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -24,7 +24,6 @@ github:
     - api-management
     - apisix
     - devops
-    - docker
 
   enabled_merge_buttons:
     squash: true
diff --git a/.dockerignore b/.dockerignore
index 3c3629e..cf70988 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1 +1 @@
-node_modules
+**/node_modules
diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE
deleted file mode 100644
index b3c0269..0000000
--- a/.github/ISSUE_TEMPLATE
+++ /dev/null
@@ -1,23 +0,0 @@
-Please answer these questions before submitting your issue.
-
-- Why do you submit this issue?
-- [ ] Question or discussion
-- [ ] Bug
-- [ ] Requirements
-- [ ] Feature or performance improvement
-- [ ] Other
-
-___
-### Question
-- What do you want to know?
-
-___
-### Bug
-- Which version of Apache APISIX Dashboard, OS, and Browser?
-
-- What happened?
-If possible, provide a way to reproduce the error.
-
-___
-### Requirements or improvement
-- Please describe your requirements or improvement suggestions.
diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md
new file mode 100644
index 0000000..cf887b7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-report.md
@@ -0,0 +1,36 @@
+---
+name: Bug report
+about: Create a bug report for the Apache APISIX Dashboard
+labels: "bug"
+---
+
+# Bug report
+
+## Describe the bug
+
+A clear and concise description of what the bug is.
+
+## How to Reproduce
+
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+## Expected behavior
+
+A clear and concise description of what you expected to happen.
+
+## Screenshots
+
+Add screenshots to help explain your problem if applicable.
+
+## System information
+
+- OS: [e.g. macOS, Windows]
+- Browser (if applies) [e.g. Chrome, Safari, Edge]
+- Version: [e.g. 2.0-rc3]
+
+## Additional context
+
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..216f73c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+  - name: Visit Apache APISIX
+    url: https://github.com/apache/apisix
+    about: Ask questions or discuss with other community members about Apache APISIX
diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md
new file mode 100644
index 0000000..aa019dc
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature-request.md
@@ -0,0 +1,23 @@
+---
+name: Feature request
+about: Create a feature request for the Apache APISIX Dashboard
+labels: 'feature'
+---
+
+# Feature request
+
+## Please describe your feature
+
+A clear and concise description of what you want and what your use case is.
+
+## Describe the solution you'd like
+
+A clear and concise description of what you want to happen.
+
+## Describe alternatives you've considered
+
+A clear and concise description of any alternative solutions or features you've considered.
+
+## Additional context
+
+Add any other context or screenshots about the feature request here.
diff --git a/.github/workflows/backend-cli-test.yml b/.github/workflows/backend-cli-test.yml
new file mode 100644
index 0000000..db7fc15
--- /dev/null
+++ b/.github/workflows/backend-cli-test.yml
@@ -0,0 +1,31 @@
+name: Backend CLI Test
+
+on:
+  push:
+    branches:
+      - master
+      - v2.0
+  pull_request:
+    branches:
+      - master
+      - v2.0
+
+jobs:
+  run-test:
+    runs-on: ubuntu-latest
+
+    services:
+      etcd:
+        image: bitnami/etcd:3.4.13
+        ports:
+          - 2379:2379
+          - 2380:2380
+        env:
+          ALLOW_NONE_AUTHENTICATION: yes
+
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: run test
+        working-directory: ./api
+        run: sudo ./test/shell/cli_test.sh
diff --git a/.github/workflows/backend-e2e-test.yml b/.github/workflows/backend-e2e-test.yml
new file mode 100644
index 0000000..1bfe4ec
--- /dev/null
+++ b/.github/workflows/backend-e2e-test.yml
@@ -0,0 +1,33 @@
+name: Backend E2E Test
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - master
+      - v2.0
+
+jobs:
+  run-test:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: setup go
+        uses: actions/setup-go@v1
+        with:
+          go-version: '1.13'
+
+      - name: run docker compose
+        working-directory: ./api/test/docker
+        run: |
+          docker-compose up -d
+          sleep 5
+          docker logs docker_managerapi_1
+
+      - name: run test
+        working-directory: ./api/test/e2e
+        run: go test
diff --git a/.github/workflows/backend-unit-test.yml b/.github/workflows/backend-unit-test.yml
new file mode 100644
index 0000000..2a8dd83
--- /dev/null
+++ b/.github/workflows/backend-unit-test.yml
@@ -0,0 +1,74 @@
+name: Backend Unit Test
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - master
+      - v2.0
+
+jobs:
+  run-test:
+    runs-on: ubuntu-latest
+
+    services:
+      etcd:
+        image: bitnami/etcd:3.4.13
+        ports:
+          - 2379:2379
+          - 2380:2380
+        env:
+          ALLOW_NONE_AUTHENTICATION: yes
+
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: get lua lib
+        run: |
+          wget https://github.com/api7/dag-to-lua/archive/v1.1.tar.gz
+          sudo mkdir -p ./api/dag-to-lua
+          tar -zxvf v1.1.tar.gz
+          sudo mv ./dag-to-lua-1.1/lib/* ./api/dag-to-lua/
+
+      - name: setup go
+        uses: actions/setup-go@v1
+        with:
+          go-version: '1.13'
+
+      - name: setup lua
+        run: |
+          sudo apt-get update
+          sudo apt-get install lua5.1
+
+      - name: run test
+        run: |
+          make api-test
+
+      - name: upload coverage profile
+        working-directory: ./api
+        run: |
+          bash <(curl -s https://codecov.io/bash)
+
+      - name: run with custom port
+        working-directory: ./api
+        run: |
+          export GO111MOUDULE=on
+          export APISIX_CONF_PATH=$PWD/conf
+          sed -i 's/8080/8088/' conf/conf.yaml
+          go build -o ./manager-api
+          ./manager-api > ./api.log 2>&1 &
+          sleep 2
+          cat ./api.log
+          cat conf/conf.yaml
+
+      - name: run with custom port
+        working-directory: ./api
+        run: |
+          curl http://127.0.0.1:8088/apisix/admin/user/login -X POST -i -d '{"username":"admin", "password": "admin"}'
+          code=$(curl -k -i -m 20 -o /dev/null -s -w %{http_code} http://127.0.0.1:8088/apisix/admin/user/login -X POST -i -d '{"username":"admin", "password": "admin"}')
+          if [ ! $code -eq 200 ]; then
+              echo "failed: failed to custom port"
+              exit 1
+          fi
diff --git a/.github/workflows/deploy-api.yml b/.github/workflows/deploy-api.yml
deleted file mode 100644
index d996e52..0000000
--- a/.github/workflows/deploy-api.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-name: Deploy API to Azure
-
-on:
-  push:
-    branches:
-      - master
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-
-    services:
-      etcd:
-        image: bitnami/etcd:3.3.13-r80
-        ports:
-          - 2379:2379
-          - 2380:2380
-        env:
-          ALLOW_NONE_AUTHENTICATION: yes
-
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: license check
-        run: |
-          make license-check
-
-      - name: get lua lib
-        run: |
-          wget https://github.com/api7/dag-to-lua/archive/v1.1.tar.gz
-          sudo mkdir -p /go/manager-api/dag-to-lua/
-          tar -zxvf v1.1.tar.gz
-          sudo mv ./dag-to-lua-1.1/lib/* /go/manager-api/dag-to-lua/
-
-      - name: install runtime
-        run: |
-          sudo apt-get update
-          sudo apt-get install lua5.1
-          sudo add-apt-repository ppa:longsleep/golang-backports
-          sudo apt update
-          export GO111MOUDULE=on
-          sudo apt install golang-1.14-go
-
-      - name: generate json schema
-        working-directory: ./api
-        run: |
-          wget https://github.com/apache/apisix/archive/master.zip
-          mkdir -p ./build-tools/apisix/
-          unzip master.zip
-          sudo mv ./apisix-master/apisix/* ./build-tools/apisix/
-          rm -rf ./apisix-master
-          cd ./build-tools/ && lua schema-sync.lua > ../conf/schema.json
-
-      - name: run test
-        working-directory: ./api
-        run: |
-          export APIX_ETCD_ENDPOINTS=127.0.0.1:2379
-          go test ./...
-
-      - uses: Azure/docker-login@v1
-        with:
-          login-server: apisixacr.azurecr.cn
-          username: ${{ secrets.REGISTRY_USERNAME }}
-          password: ${{ secrets.REGISTRY_PASSWORD }}
-
-      - name: build and push docker image
-        working-directory: ./api
-        run: |
-          docker build . -t apisixacr.azurecr.cn/managerapi:${{ github.sha }}
-          docker push apisixacr.azurecr.cn/managerapi:${{ github.sha }}
diff --git a/.github/workflows/deploy-frontend.yml b/.github/workflows/deploy-frontend.yml
deleted file mode 100644
index 9998d50..0000000
--- a/.github/workflows/deploy-frontend.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: Deploy frontend to Azure
-
-on:
-  push:
-    branches: [master]
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-
-    steps:
-      - uses: actions/checkout@v2
-
-      - uses: Azure/docker-login@v1
-        with:
-          login-server: apisixacr.azurecr.cn
-          username: ${{ secrets.REGISTRY_USERNAME }}
-          password: ${{ secrets.REGISTRY_PASSWORD }}
-
-      - run: |
-          docker build . -t apisixacr.azurecr.cn/dashboard:${{ github.sha }}
-          docker push apisixacr.azurecr.cn/dashboard:${{ github.sha }}
diff --git a/.github/workflows/deploy-with-docker.yml b/.github/workflows/deploy-with-docker.yml
new file mode 100644
index 0000000..2dfa41d
--- /dev/null
+++ b/.github/workflows/deploy-with-docker.yml
@@ -0,0 +1,44 @@
+name: Test and Deploy with Docker
+
+on:
+  push:
+    branches:
+      - master
+      - v2.0
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v2
+
+      - uses: Azure/docker-login@v1
+        with:
+          login-server: apisixacr.azurecr.cn
+          username: ${{ secrets.REGISTRY_USERNAME }}
+          password: ${{ secrets.REGISTRY_PASSWORD }}
+
+      - name: Build Docker Image
+        run: |
+          docker build -t dashboard:ci .
+
+      - name: Modify ETCD IP
+        run: |
+          sed -i 's/127.0.0.1:2379/172.16.238.10:2379/' api/conf/conf.yaml
+          sed -i 's/host: 127.0.0.1/host: 0.0.0.0/' api/conf/conf.yaml
+
+      - name: Run Docker Compose
+        working-directory: ./api/test/docker-deploy
+        run: |
+          docker-compose up -d
+          sleep 5
+          docker logs docker-deploy_managerapi_1
+
+      - name: Run Test
+        run: api/test/shell/docker_deploy_test.sh
+
+      - name: Deploy
+        run: |
+          docker tag dashboard:ci apisixacr.azurecr.cn/dashboard:${{ github.sha }}
+          docker push apisixacr.azurecr.cn/dashboard:${{ github.sha }}
diff --git a/.github/workflows/frontend-e2e-test.yml b/.github/workflows/frontend-e2e-test.yml
new file mode 100644
index 0000000..1753ac2
--- /dev/null
+++ b/.github/workflows/frontend-e2e-test.yml
@@ -0,0 +1,40 @@
+name: Frontend e2e test
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - master
+      - v2.0
+defaults:
+  run:
+    working-directory: web
+
+jobs:
+  web-e2e:
+    name: Frontend e2e test
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Cache or restore node_modules
+        id: node_modules_cache_id
+        uses: actions/cache@v2
+        with:
+          path: "**/node_modules"
+          key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }}
+
+      - name: Setup Node.js environment
+        uses: actions/setup-node@v1
+        with:
+          node-version: 14.x
+
+      - name: Install dependencies
+        if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
+        run: yarn install
+
+      - name: Start frontend then test
+        run: yarn test:e2e
diff --git a/.github/workflows/license-checker.yml b/.github/workflows/go-lint.yml
similarity index 59%
copy from .github/workflows/license-checker.yml
copy to .github/workflows/go-lint.yml
index 09b2cba..0de9fe3 100644
--- a/.github/workflows/license-checker.yml
+++ b/.github/workflows/go-lint.yml
@@ -1,20 +1,20 @@
-name: License checker
-
+name: go-lint
 on:
   push:
     branches:
       - master
+      - v2.0
   pull_request:
     branches:
       - master
+      - v2.0
 
 jobs:
-  check-license:
+  golangci:
     runs-on: ubuntu-latest
 
     steps:
       - uses: actions/checkout@v2
 
-      - name: run license check
-        run: |
-          make license-check
+      - name: run lint
+        run: make go-lint
diff --git a/.github/workflows/license-checker.yml b/.github/workflows/license-checker.yml
index 09b2cba..b6e6c75 100644
--- a/.github/workflows/license-checker.yml
+++ b/.github/workflows/license-checker.yml
@@ -7,6 +7,7 @@ on:
   pull_request:
     branches:
       - master
+      - v2.0
 
 jobs:
   check-license:
diff --git a/.github/workflows/test-api.yml b/.github/workflows/test-api.yml
deleted file mode 100644
index 386c52d..0000000
--- a/.github/workflows/test-api.yml
+++ /dev/null
@@ -1,61 +0,0 @@
-name: Test API
-
-on:
-  push:
-    branches:
-      - master
-  pull_request:
-    branches:
-      - master
-
-jobs:
-  run-test:
-    runs-on: ubuntu-latest
-
-    services:
-      etcd:
-        image: bitnami/etcd:3.4.13
-        ports:
-          - 2379:2379
-          - 2380:2380
-        env:
-          ALLOW_NONE_AUTHENTICATION: yes
-
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: run Makefile
-        run: |
-          make license-check
-
-      - name: get lua lib
-        run: |
-          wget https://github.com/api7/dag-to-lua/archive/v1.1.tar.gz
-          sudo mkdir -p /go/manager-api/dag-to-lua/
-          tar -zxvf v1.1.tar.gz
-          sudo mv ./dag-to-lua-1.1/lib/* /go/manager-api/dag-to-lua/
-
-      - name: install runtime
-        run: |
-          sudo apt-get update
-          sudo apt-get install lua5.1
-          sudo add-apt-repository ppa:longsleep/golang-backports
-          sudo apt update
-          export GO111MOUDULE=on
-          sudo apt install golang-1.14-go
-
-      - name: generate json schema
-        working-directory: ./api
-        run: |
-          wget https://github.com/apache/apisix/archive/master.zip
-          mkdir -p ./build-tools/apisix/
-          unzip master.zip
-          sudo mv ./apisix-master/apisix/* ./build-tools/apisix/
-          rm -rf ./apisix-master
-          cd ./build-tools/ && lua schema-sync.lua > ../conf/schema.json
-
-      - name: run test
-        working-directory: ./api
-        run: |
-          export APIX_ETCD_ENDPOINTS=127.0.0.1:2379
-          go test ./...
diff --git a/.github/workflows/test-deploy-with-go.yml b/.github/workflows/test-deploy-with-go.yml
deleted file mode 100644
index 59fefbb..0000000
--- a/.github/workflows/test-deploy-with-go.yml
+++ /dev/null
@@ -1,73 +0,0 @@
-name: Test deploy frontend and API using Go
-
-on:
-  push:
-    branches:
-      - master
-  pull_request:
-    branches:
-      - master
-
-jobs:
-  frontend:
-    name: Front-End compile
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: Setup Node.JS environment for Front-End
-        uses: actions/setup-node@v1
-        with:
-          node-version: 14.x
-
-      - name: Install Front-End dependencies
-        run: yarn install
-
-      - name: Build Front-End
-        run: yarn build
-
-      - name: Store Compiled Front-End files
-        uses: actions/upload-artifact@v2
-        with:
-          name: dist
-          path: ./dist/
-
-  backend:
-    name: Back-End compile
-    needs: frontend
-    runs-on: ubuntu-latest
-
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: Copy file to Back-End compile environment
-        uses: actions/download-artifact@v2
-        with:
-          name: dist
-          path: ./api/dashboard/
-
-      - name: Setup Golang environment for Back-End
-        uses: actions/setup-go@v2
-        with:
-          go-version: 1.14
-
-      - name: Setup Build Environment
-        run: |
-          cd ./api/
-          sudo go get github.com/rakyll/statik
-
-      - name: Generate Built-in Code
-        run: |
-          cd ./api/
-          $(go env GOPATH)/bin/statik -src=./dashboard/
-
-      - name: Build Back-End Binary
-        run: |
-          cd ./api/
-          sudo go build -o /go/manager-api/manager-api
-
-      - name: Store Compiled Back-End files
-        uses: actions/upload-artifact@v2
-        with:
-          name: output
-          path: /go/manager-api/manager-api
diff --git a/.github/workflows/test-frontend-multiple-node-build.yml b/.github/workflows/test-frontend-multiple-node-build.yml
index b631398..1c6aa38 100644
--- a/.github/workflows/test-frontend-multiple-node-build.yml
+++ b/.github/workflows/test-frontend-multiple-node-build.yml
@@ -1,6 +1,6 @@
 # This is a basic test to build the dashboard
 
-name: Test building frontend in multiple node version
+name: Test building web in multiple node version
 
 # Controls when the action will run. Triggers the workflow on push or pull request
 # events but only for the master branch
@@ -11,6 +11,7 @@ on:
   pull_request:
     branches:
       - master
+      - v2.0
 
 # A workflow run is made up of one or more jobs that can run sequentially or in parallel
 jobs:
@@ -34,10 +35,13 @@ jobs:
 
       # Install dependencies
       - name: Install dependencies
+        working-directory: web
         run: yarn
 
-      - name: Build the Dashboard
-        run: yarn build
-
       - name: Lint
+        working-directory: web
         run: yarn run lint:js && yarn run lint:style
+
+      - name: Build the Dashboard
+        working-directory: web
+        run: yarn build
diff --git a/.gitignore b/.gitignore
index 571eb5b..06ac7fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,8 +7,7 @@
 _roadhog-api-doc
 
 # production
-/dist
-/.vscode
+dist
 
 # misc
 .DS_Store
@@ -19,7 +18,6 @@ yarn-error.log
 .idea
 package-lock.json
 *bak
-.vscode
 
 # visual studio code
 .history
@@ -42,5 +40,8 @@ build
 /compose/**/nginx.pid
 /compose/etcd_data
 manager-api
-conf.json
-conf.json-e
+
+output
+default.etcd
+api/build-tools/apisix
+/*.zip
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..e521849
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,8 @@
+{
+  "eslint.validate": [
+    "javascript",
+    "javascriptreact",
+    { "language": "typescript", "autoFix": true },
+    { "language": "typescriptreact", "autoFix": true }
+  ]
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4553526..a6a0fa2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,9 +19,33 @@
 
 # Table of Contents
 
+- [2.0.0](#200)
 - [1.5.0](#150)
 - [1.0.0](#100)
 
+# 2.0.0
+
+This is a release candidate.
+
+# Core
+
+- Refactor frontend with Admin-API.
+- Manager-API removes dependency on MySQL.
+- Support plugin orchestration.
+- Setting standards for frontend internationalization.
+- New deployment pattern.
+- Add more test cases.
+- Document Enhancement.
+- Add back-end E2E test examples.
+- Improve CI testing.
+- Support log save to local file.
+- Optimize the deployment process.
+- Add E2E test examples to the frontend. [#619](https://github.com/apache/apisix-dashboard/pull/619)
+- Fix the Promethues plugin updating incorrect values when updating routes. [#666](https://github.com/apache/apisix-dashboard/pull/666)
+- Fix page display exceptions when the Redirect option is selected as Enable HTTPS in the Route page. [#692](https://github.com/apache/apisix-dashboard/pull/692)
+
+For more changes, please refer to [Milestone](https://github.com/apache/apisix-dashboard/milestone/4)
+
 # 1.5.0
 
 This release mainly refactors the dashboard.
diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md
index 774e199..378f9df 100644
--- a/CHANGELOG.zh-CN.md
+++ b/CHANGELOG.zh-CN.md
@@ -19,9 +19,35 @@
 
 # 目录
 
+- [2.0.0](#200)
 - [1.5.0](#150)
 - [1.0.0](#100)
 
+
+# 2.0.0
+
+这是一个候选版本。
+
+### 核心
+
+- 前端根据新的 admin-api 进行重构。
+- 后端移除对 mysql 的依赖。
+- 支持插件编排模式。
+- 制定前端国际化标准。
+- Dashboard 新的部署方式。
+- 增加更多测试用例。
+- 文档增强。
+- 添加后端 E2E 测试例子。
+- 完善 CI 测试。
+- 支持日志保存到本地文件。
+- 优化部署流程。
+- 前端增加 E2E 测试例子。[#619](https://github.com/apache/apisix-dashboard/pull/619)
+- 修复 Promethues 插件在更新路由时更新错误值的问题。[#666](https://github.com/apache/apisix-dashboard/pull/666)
+- 修复在路由页面中重定向选项选择为 启用HTTPS 时页面显示异常的问题。[#692](https://github.com/apache/apisix-dashboard/pull/692)
+
+
+更多的变动可以参考[里程碑](https://github.com/apache/apisix-dashboard/milestone/4)
+
 # 1.5.0
 
 该版本主要完成 Dashboard 的重构工作。
diff --git a/Dockerfile b/Dockerfile
index 90d4fde..a1e219f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -14,24 +14,71 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+FROM alpine:latest as pre-build
 
-# phase-build
-FROM node:12 as builder
+ARG APISIX_DASHBOARD_VERSION=v2.0
 
-WORKDIR /usr/src/app/
-USER root
+RUN set -x \
+    && wget https://github.com/apache/apisix-dashboard/archive/${APISIX_DASHBOARD_VERSION}.tar.gz -O /tmp/apisix-dashboard.tar.gz \
+    && mkdir /usr/local/apisix-dashboard \
+    && tar -xvf /tmp/apisix-dashboard.tar.gz -C /usr/local/apisix-dashboard --strip 1
 
-COPY package.json ./
-COPY yarn.lock ./
-RUN yarn
+FROM golang:1.14 as api-builder
 
-COPY ./ ./
-RUN yarn build && rm -rf /usr/src/app/node_modules
+ARG ENABLE_PROXY=false
 
-# phase-run
-FROM nginx:1.16-alpine
+WORKDIR /usr/local/apisix-dashboard
 
-COPY ./docker/nginx.conf /etc/nginx/conf.d/default.conf
-COPY --from=builder /usr/src/app/dist /usr/share/nginx/html
+COPY --from=pre-build /usr/local/apisix-dashboard .
 
-EXPOSE 80
+WORKDIR /usr/local/apisix-dashboard/api
+
+RUN mkdir -p ../output/conf \
+    && cp ./conf/*.json ../output/conf
+
+RUN wget https://github.com/api7/dag-to-lua/archive/v1.1.tar.gz -O /tmp/v1.1.tar.gz \
+    && mkdir /tmp/dag-to-lua \
+    && tar -xvf /tmp/v1.1.tar.gz -C /tmp/dag-to-lua --strip 1 \
+    && mkdir -p ../output/dag-to-lua \
+    && mv /tmp/dag-to-lua/lib/* ../output/dag-to-lua/
+
+RUN if [ "$ENABLE_PROXY" = "true" ] ; then go env -w GOPROXY=https://goproxy.io,direct ; fi
+
+RUN go env -w GO111MODULE=on \
+    && CGO_ENABLED=0 go build -o ../output/manager-api .
+
+FROM node:14-alpine as fe-builder
+
+ARG ENABLE_PROXY=false
+
+WORKDIR /usr/local/apisix-dashboard
+
+COPY --from=pre-build /usr/local/apisix-dashboard .
+
+WORKDIR /usr/local/apisix-dashboard/web
+
+RUN if [ "$ENABLE_PROXY" = "true" ] ; then yarn config set registry https://registry.npm.taobao.org/ ; fi
+
+RUN yarn install
+
+RUN yarn build
+
+FROM alpine:latest as prod
+
+ARG ENABLE_PROXY=false
+
+RUN if [ "$ENABLE_PROXY" = "true" ] ; then sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories ; fi
+
+RUN apk add lua5.1
+
+WORKDIR /usr/local/apisix-dashboard
+
+COPY --from=api-builder /usr/local/apisix-dashboard/output/ ./
+
+COPY --from=fe-builder /usr/local/apisix-dashboard/output/ ./
+
+RUN mkdir logs
+
+EXPOSE 8080
+
+CMD [ "/usr/local/apisix-dashboard/manager-api" ]
diff --git a/Makefile b/Makefile
index c55fedb..7eee57c 100644
--- a/Makefile
+++ b/Makefile
@@ -15,11 +15,105 @@
 # limitations under the License.
 #
 
-### license-check:    Check apisix-dashboard source codes for Apache License
+SHELL := /bin/bash -o pipefail
+UNAME ?= $(shell uname)
+YARN_EXEC ?= $(shell which yarn)
+GO_EXEC ?= $(shell which go)
+
+VERSION ?= latest
+RELEASE_SRC = apache-apisix-dashboard-${VERSION}-src
+
+export GO111MODULE=on
+
+### help:		Show Makefile rules
+.PHONY: help
+help:
+	@echo Makefile rules:
+	@echo
+	@grep -E '^### [-A-Za-z0-9_]+:' Makefile | sed 's/###/   /'
+
+
+### build:		Build Apache APISIX Dashboard, it contains web and manager-api
+.PHONY: build
+build: web-default api-default
+	api/build.sh && cd ./web && export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true && yarn install && yarn build  && mkdir -p ../output/logs
+
+
+.PHONY: web-default
+web-default:
+ifeq ("$(wildcard $(YARN_EXEC))", "")
+	@echo "ERROR: Need to install yarn first"
+	exit 1
+endif
+
+
+.PHONY: api-default
+api-default:
+ifeq ("$(wildcard $(GO_EXEC))", "")
+	@echo "ERROR: Need to install golang 1.13+ first"
+	exit 1
+endif
+
+
+### api-test:		Run the tests of manager-api 
+.PHONY: api-test
+api-test: api-default
+	cd api/ && APISIX_API_WORKDIR=$$PWD go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
+
+
+### api-run:		Run the manager-api
+.PHONY: api-run
+api-run: api-default
+	cd api/ && go run .
+
+### api-stop:		Stop the manager-api
+api-stop:
+	kill $(ps aux | grep 'manager-api' | awk '{print $2}')
+
+
+### go-lint:		Lint Go source code
+.PHONY: go-lint
+go-lint: ## Run the golangci-lint application (install if not found)
+	@#Brew - MacOS
+	@if [ "$(shell command -v golangci-lint)" = "" ] && [ "$(shell command -v brew)" != "" ] && [ "$(UNAME)" = "Darwin" ]; then brew install golangci-lint; fi;
+	@#has sudo
+	@if [ "$(shell command -v golangci-lint)" = "" ]; then curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.32.0 && sudo cp ./bin/golangci-lint $(go env GOPATH)/bin/; fi;
+	@echo "running golangci-lint..."
+	@cd api && golangci-lint run --tests=false ./...
+
+
+### license-check:	Check Apache APISIX Dashboard Source Codes for Apache License
 .PHONY: license-check
 license-check:
 ifeq ("$(wildcard .actions/openwhisk-utilities/scancode/scanCode.py)", "")
 	git clone https://github.com/apache/openwhisk-utilities.git .actions/openwhisk-utilities
 	cp .actions/ASF* .actions/openwhisk-utilities/scancode/
 endif
-	.actions/openwhisk-utilities/scancode/scanCode.py --config .actions/ASF-Release.cfg ./
\ No newline at end of file
+	.actions/openwhisk-utilities/scancode/scanCode.py --config .actions/ASF-Release.cfg ./
+
+
+.PHONY: release-src
+release-src:
+	git clean -Xdf
+	tar -zcvf $(RELEASE_SRC).tgz \
+	--exclude .github \
+	--exclude .git \
+	--exclude .gitattributes \
+	--exclude .idea \
+	--exclude .vscode \
+	--exclude .gitignore \
+	--exclude .DS_Store \
+	--exclude docs \
+	--exclude release \
+	--exclude api/internal/core/store/validate_mock.go \
+	--exclude api/internal/core/storage/storage_mock.go \
+	.
+
+	gpg --batch --yes --armor --detach-sig $(RELEASE_SRC).tgz
+	shasum -a 512 $(RELEASE_SRC).tgz > $(RELEASE_SRC).tgz.sha512
+
+	mkdir -p release
+	mv $(RELEASE_SRC).tgz release/$(RELEASE_SRC).tgz
+	mv $(RELEASE_SRC).tgz.asc release/$(RELEASE_SRC).tgz.asc
+	mv $(RELEASE_SRC).tgz.sha512 release/$(RELEASE_SRC).tgz.sha512
+
diff --git a/README.md b/README.md
index ff68f39..ac4e185 100644
--- a/README.md
+++ b/README.md
@@ -21,35 +21,64 @@ English | [简体中文](./README.zh-CN.md)
 
 # Apache APISIX Dashboard
 
-Dashboard for [Apache APISIX](https://github.com/apache/apisix)
+## What's Apache APISIX Dashboard
 
-[Online Demo](http://139.217.190.60/), Username/Password: `admin`.
+The Apache APISIX Dashboard is designed to make it as easy as possible for users to operate [Apache APISIX](https://github.com/apache/apisix) through a frontend interface.
 
-## User Guide
+The Dashboard is the control plane and performs all parameter checks; Apache APISIX mixes data and control planes and will evolve to a pure data plane.
+
+This project includes `manager-api`, which will gradually replace `admin-api` in Apache APISIX.
+
+Note: Currently the Dashboard does not have complete coverage of Apache APISIX features, [visit here](https://github.com/apache/apisix-dashboard/milestones) to view the milestones.
+
+![architecture](./docs/images/architecture.png)
+
+## Project structure
 
-Please refer to [User Guide](./docs/USER_GUIDE.md)
+```
+.
+├── CHANGELOG.md
+├── CHANGELOG.zh-CN.md
+├── CODE_OF_CONDUCT.md
+├── CONTRIBUTING.md
+├── Dockerfile
+├── LICENSE
+├── Makefile
+├── NOTICE
+├── README.md
+├── README.zh-CN.md
+├── api
+├── docs
+├── licenses
+└── web
+```
 
-## Deployment
+1. The `api` directory is used to store the `manager-api` source codes, which is used to manage `etcd` and provide APIs to the frontend interface.
+2. The `web` directory is used to store the frontend source codes.
 
-- [Deploy Manually](./docs/deploy.md)
-- [Deploy with Docker](./compose/README.md)
+## Build then launch
+
+Support the following ways currently.
+
+- [Source Codes](./docs/deploy.md)
+- [Docker](./docs/deploy-with-docker.md)
 
 ## Development
 
-- [Apache APISIX](https://github.com/apache/apisix)
-- [Dashboard](./docs/develop.md)
+Please refer to the [Development Guide](./docs/develop.md).
 
-## Milestones
+## User Guide
 
-- [2.0](https://github.com/apache/apisix-dashboard/milestone/4)
-- [2.1](https://github.com/apache/apisix-dashboard/milestone/5)
+Please refer to the [User Guide](./docs/USER_GUIDE.md).
 
 ## Contributing
 
-See [CONTRIBUTING](./CONTRIBUTING.md) for details on submitting patches and the contribution workflow.
+Please refer to the [Contribution Guide](./CONTRIBUTING.md) for a more detailed infomation.
 
 ## FAQ
 
-1. If you need the dashboard-1.0 which is built with Vue.js, please refer to [master-vue](https://github.com/apache/apisix-dashboard/tree/master-vue).
+Please refer to the [FAQ](./docs/FAQ.md) for more known issues.
+
+## License
 
-2. The dashboard 2.0 removes MySQL which [dashboard 1.5](https://github.com/apache/apisix-dashboard/tree/backup-1.5-latest) is relied on.
+[Apache License 2.0](./LICENSE)
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 9b24761..1d08b84 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -19,38 +19,66 @@
 
 [English](./README.md) | 简体中文
 
-# [Apache APISIX](https://github.com/apache/apisix) 控制台
+# Apache APISIX Dashboard
 
-访问 [http://139.217.190.60/](http://139.217.190.60/) 查看在线预览,账户与密码:`admin`。
+## 介绍
 
-## 用户指南
+Apache APISIX Dashboard 旨在通过前端界面,让用户尽可能更方便地操作 [Apache APISIX](https://github.com/apache/apisix)。
 
-请参考 [用户指南](./docs/USER_GUIDE.zh-CN.md)
+Dashboard 为控制平面,完成所有参数的校验;Apache APISIX 混合了数据平面与控制平面,会逐渐演进为纯粹的数据平面。
 
-## 部署
+本项目包含了 `manager-api` 与前端界面,前者将逐渐替代 Apache APISIX 中的 `admin-api`,我们正在积极地迁移相关逻辑。
 
-当前支持如下方式部署:
+注意:目前 Dashboard 尚未完整覆盖 Apache APISIX 的功能,[访问此处](https://github.com/apache/apisix-dashboard/milestones)以查看里程碑。
 
-- [手动部署](./docs/deploy.zh-CN.md)
-- [使用 Docker 部署](./compose/README.md)
+![architecture](./docs/images/architecture.png)
 
-## 开发
+## 目录结构
 
-开发分为 Apache APISIX 开发、Dashboard 开发
+```
+.
+├── CHANGELOG.md
+├── CHANGELOG.zh-CN.md
+├── CODE_OF_CONDUCT.md
+├── CONTRIBUTING.md
+├── Dockerfile
+├── LICENSE
+├── Makefile
+├── NOTICE
+├── README.md
+├── README.zh-CN.md
+├── api
+├── docs
+├── licenses
+└── web
+```
 
-- [Apache APISIX](https://github.com/apache/apisix)
-- [Dashboard](./docs/develop.zh-CN.md)
+1. `api` 目录用于存放 `manager-api` 源码,它用于管理 `etcd` 并为前端界面提供接口。
+2. `web` 目录用于存放前端源码。
 
-## 里程碑
+## 构建并启动
 
-- [2.0](https://github.com/apache/apisix-dashboard/milestone/4)
-- [2.1](https://github.com/apache/apisix-dashboard/milestone/5)
+支持以下方式:
 
-## 贡献
+- [源码](./docs/deploy.zh-CN.md)
+- [Docker](./docs/deploy-with-docker.zh-CN.md)
 
-请参考[贡献指南](./CONTRIBUTING.md)以获取更详细的流程。
+## 本地开发
+
+请参考[开发指南](./docs/develop.zh-CN.md)
+
+## 使用指南
+
+请参考[用户指南](./docs/USER_GUIDE.zh-CN.md)
+
+## 参与贡献
+
+请参考[贡献指南](./CONTRIBUTING.md)以获取更详细的流程
 
 ## FAQ
 
-1. 如果你需要 Vue.js 构建的 dashboard-1.0,请使用 [master-vue 分支](https://github.com/apache/apisix-dashboard/tree/master-vue)。
-2. 2.0 版本的控制台移除了[1.5 版本](https://github.com/apache/apisix-dashboard/tree/backup-1.5-latest)中的 MySQL,将直接操作 etcd。
+请参考 [FAQ 汇总](./docs/FAQ.zh-CN.md)以查看更多已知问题
+
+## License
+
+[Apache License 2.0](./LICENSE)
diff --git a/api/build-tools/schema-sync.lua b/api/build-tools/schema-sync.lua
index 257d220..aa65c2b 100644
--- a/api/build-tools/schema-sync.lua
+++ b/api/build-tools/schema-sync.lua
@@ -29,6 +29,7 @@ local fake_module_list = {
     'pb',
     'prometheus',
     'protoc',
+    'skywalking.tracer',
 
     'resty.cookie',
     'resty.core.regex',
@@ -61,7 +62,8 @@ local fake_module_list = {
     'apisix.plugins.skywalking.tracer',
     'apisix.plugins.zipkin.codec',
     'apisix.plugins.zipkin.random_sampler',
-    'apisix.plugins.zipkin.reporter'
+    'apisix.plugins.zipkin.reporter',
+    'apisix.timers'
 }
 for _, name in ipairs(fake_module_list) do
     package.loaded[name] = {}
@@ -77,7 +79,11 @@ ngx.re = {}
 ngx.timer = {}
 ngx.location = {}
 ngx.socket = {}
+ngx.thread = {}
 ngx.re.gmatch = empty_function
+ngx.shared = {
+    ["plugin-api-breaker"] = {}
+}
 
 -- additional define for management
 local time_def = {
@@ -127,7 +133,12 @@ local plugins = get_plugin_list()
 for idx, plugin_name in pairs(plugins) do
     local plugin = require("apisix.plugins." .. plugin_name)
     if plugin and type(plugin) == "table" and plugin.schema then
-        schema_all.plugins[plugin_name] = plugin.schema
+        schema_all.plugins[plugin_name]= {
+            ['schema'] = plugin.schema
+        }
+    end
+    if plugin and type(plugin) == "table" and plugin.consumer_schema then
+        schema_all.plugins[plugin_name]['consumer_schema'] = plugin.consumer_schema
     end
 end
 
diff --git a/api/entry.sh b/api/build-tools/schema-sync.sh
similarity index 54%
copy from api/entry.sh
copy to api/build-tools/schema-sync.sh
index 8f7afbd..a9a856d 100755
--- a/api/entry.sh
+++ b/api/build-tools/schema-sync.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/usr/bin/env bash
 #
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
@@ -16,22 +16,33 @@
 # limitations under the License.
 #
 
-pwd=`pwd`
-
-# config
-cp ${pwd}/api/conf/conf_preview.json ${pwd}/conf.json
-
-# export APIX_DAG_LIB_PATH="${pwd}/dag-to-lua-1.1/lib/"
-# export APIX_ETCD_ENDPOINTS="127.0.0.1:2379"
+set -ex
 
-export SYSLOG_HOST=127.0.0.1
+pwd=`pwd`
 
-if [[ "$unamestr" == 'Darwin' ]]; then
-	sed -i '' -e "s%#syslogAddress#%`echo $SYSLOG_HOST`%g" ${pwd}/conf.json
+if [[ -n $1 && -d "$1" ]]; then
+  path=$1
+  if [[ -d "${path}/apisix" ]]; then
+    cp -R ${path}/apisix/ ./api/build-tools/apisix/
+  else
+    cp -R ${path}/ ./api/build-tools/apisix/
+  fi
 else
-	sed -i -e "s%#syslogAddress#%`echo $SYSLOG_HOST`%g" ${pwd}/conf.json
+  version="master"
+  if [[ -n $1 ]]; then
+  version=$1
+  fi
+  wget -O $version.zip https://github.com/apache/apisix/archive/$version.zip
+
+  unzip $version.zip
+  mkdir -p ./api/build-tools/apisix/
+  cp -R ./apisix-$version/apisix/ ./api/build-tools/
+  rm -rf ./apisix-$version
 fi
 
-cp ${pwd}/conf.json ${pwd}/api/conf/conf.json
+cd ./api/build-tools/ && lua schema-sync.lua > ${pwd}/api/conf/schema.json && cd ../../
+
+rm -rf ./api/build-tools/apisix/
 
-exec ./manager-api
+echo "sync success:"
+echo "${pwd}/api/conf/schema.json"
diff --git a/compose/manager_conf/entry.sh b/api/build.sh
similarity index 58%
rename from compose/manager_conf/entry.sh
rename to api/build.sh
index 34ca263..811eb84 100755
--- a/compose/manager_conf/entry.sh
+++ b/api/build.sh
@@ -1,3 +1,4 @@
+#!/usr/bin/env bash
 #
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
@@ -14,25 +15,23 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
-#!/bin/sh
-
+set -ex
+export ENV=local
 pwd=`pwd`
 
-# config
-cp ${pwd}/api/conf/conf_preview.json ${pwd}/conf.json
-
-export APIX_ETCD_ENDPOINTS="192.17.5.10:2379"
+rm -rf output && mkdir -p output/conf && mkdir -p output/dag-to-lua
 
-export SYSLOG_HOST=127.0.0.1
-
-if [[ "$unamestr" == 'Darwin' ]]; then
-	sed -i '' -e "s%#syslogAddress#%`echo $SYSLOG_HOST`%g" ${pwd}/conf.json
-else
-	sed -i -e "s%#syslogAddress#%`echo $SYSLOG_HOST`%g" ${pwd}/conf.json
+# get dag-to-lua lib
+if [[ ! -f "dag-to-lua-1.1/lib/dag-to-lua.lua" ]]; then
+    wget https://github.com/api7/dag-to-lua/archive/v1.1.tar.gz -P /tmp
+    tar -zxvf /tmp/v1.1.tar.gz -C /tmp
+    cp -r /tmp/dag-to-lua-1.1/lib/* ./output/dag-to-lua
 fi
 
-cp ${pwd}/conf.json ${pwd}/api/conf/conf.json
+# build
+cd ./api && go build -o ../output/manager-api . && cd ..
+
+cp ./api/conf/schema.json ./output/conf/schema.json
+cp ./api/conf/conf.yaml ./output/conf/conf.yaml
 
-cd /go/manager-api
-exec ./manager-api
+echo "Build the Manager API successfully"
diff --git a/api/conf/conf.go b/api/conf/conf.go
index 84c509f..91eb89b 100644
--- a/api/conf/conf.go
+++ b/api/conf/conf.go
@@ -17,114 +17,163 @@
 package conf
 
 import (
+	"flag"
 	"fmt"
 	"io/ioutil"
+	"log"
 	"os"
 	"path/filepath"
-	"runtime"
 
 	"github.com/tidwall/gjson"
+	"gopkg.in/yaml.v2"
 
 	"github.com/apisix/manager-api/internal/utils"
 )
 
 const (
-	ServerPort = 8080
-	WebDir     = "./dist"
-
 	EnvPROD  = "prod"
 	EnvBETA  = "beta"
 	EnvDEV   = "dev"
 	EnvLOCAL = "local"
 
-	confPath   = "/go/manager-api/conf.json"
-	schemaPath = "/go/manager-api/schema.json"
+	WebDir = "./html"
 )
 
 var (
-	ENV        string
-	basePath   string
-	Schema     gjson.Result
-	DagLibPath = "/go/manager-api/dag-to-lua/"
+	ENV              string
+	Schema           gjson.Result
+	WorkDir          = "."
+	ServerHost       = "127.0.0.1"
+	ServerPort       = 80
+	ETCDEndpoints    = []string{"127.0.0.1:2379"}
+	ErrorLogLevel    = "warn"
+	ErrorLogPath     = "logs/error.log"
+	UserList         = make(map[string]User, 2)
+	AuthConf         Authentication
+	SSLDefaultStatus = 1 //enable ssl by default
 )
 
-func init() {
-	setEnvironment()
-	initAuthentication()
-	initSchema()
+type Etcd struct {
+	Endpoints []string
 }
 
-func setEnvironment() {
-	if env := os.Getenv("ENV"); env == "" {
-		ENV = EnvLOCAL
-	} else {
-		ENV = env
-	}
-
-	if env := os.Getenv("APIX_DAG_LIB_PATH"); env != "" {
-		DagLibPath = env
-	}
+type Listen struct {
+	Host string
+	Port int
+}
 
-	_, basePath, _, _ = runtime.Caller(1)
+type ErrorLog struct {
+	Level    string
+	FilePath string `yaml:"file_path"`
 }
 
-func configurationPath() string {
-	if ENV == EnvLOCAL {
-		return filepath.Join(filepath.Dir(basePath), "conf.json")
-	} else {
-		return confPath
-	}
+type Log struct {
+	ErrorLog ErrorLog `yaml:"error_log"`
 }
 
-func getSchemaPath() string {
-	if ENV == EnvLOCAL {
-		return filepath.Join(filepath.Dir(basePath), "schema.json")
-	} else {
-		return schemaPath
-	}
+type Conf struct {
+	Etcd   Etcd
+	Listen Listen
+	Log    Log
 }
 
-type user struct {
+type User struct {
 	Username string
 	Password string
 }
 
-type authenticationConfig struct {
-	Session struct {
-		Secret     string
-		ExpireTime uint64
-	}
+type Authentication struct {
+	Secret     string
+	ExpireTime int `yaml:"expire_time"`
+	Users      []User
 }
 
-var UserList = make(map[string]user, 1)
+type Config struct {
+	Conf           Conf
+	Authentication Authentication
+}
 
-var AuthenticationConfig authenticationConfig
+func init() {
+	//go test
+	if workDir := os.Getenv("APISIX_API_WORKDIR"); workDir != "" {
+		WorkDir = workDir
+	} else {
+		flag.StringVar(&WorkDir, "p", ".", "current work dir")
+		flag.Parse()
+	}
 
-func initAuthentication() {
-	filePath := configurationPath()
-	configurationContent, err := ioutil.ReadFile(filePath)
-	if err != nil {
+	setConf()
+	setEnvironment()
+	initSchema()
+}
+
+func setConf() {
+	filePath := WorkDir + "/conf/conf.yaml"
+	if configurationContent, err := ioutil.ReadFile(filePath); err != nil {
 		panic(fmt.Sprintf("fail to read configuration: %s", filePath))
+	} else {
+		//configuration := gjson.ParseBytes(configurationContent)
+		config := Config{}
+		err := yaml.Unmarshal(configurationContent, &config)
+		if err != nil {
+			log.Printf("conf: %s, error: %v", configurationContent, err)
+		}
+
+		//listen
+		if config.Conf.Listen.Port != 0 {
+			ServerPort = config.Conf.Listen.Port
+		}
+
+		if config.Conf.Listen.Host != "" {
+			ServerHost = config.Conf.Listen.Host
+		}
+
+		//etcd
+		if len(config.Conf.Etcd.Endpoints) > 0 {
+			ETCDEndpoints = config.Conf.Etcd.Endpoints
+		}
+
+		//error log
+		if config.Conf.Log.ErrorLog.Level != "" {
+			ErrorLogLevel = config.Conf.Log.ErrorLog.Level
+		}
+		if config.Conf.Log.ErrorLog.FilePath != "" {
+			ErrorLogPath = config.Conf.Log.ErrorLog.FilePath
+		}
+		if !filepath.IsAbs(ErrorLogPath) {
+			ErrorLogPath, err = filepath.Abs(WorkDir + "/" + ErrorLogPath)
+			if err != nil {
+				panic(err)
+			}
+		}
+
+		//auth
+		initAuthentication(config.Authentication)
 	}
+}
 
-	configuration := gjson.ParseBytes(configurationContent)
-	userList := configuration.Get("authentication.user").Array()
-	// create user list
-	for _, item := range userList {
-		username := item.Map()["username"].String()
-		password := item.Map()["password"].String()
-		UserList[item.Map()["username"].String()] = user{Username: username, Password: password}
+func setEnvironment() {
+	ENV = EnvPROD
+	if env := os.Getenv("ENV"); env != "" {
+		ENV = env
 	}
-	AuthenticationConfig.Session.Secret = configuration.Get("authentication.session.secret").String()
-	if "secret" == AuthenticationConfig.Session.Secret {
-		AuthenticationConfig.Session.Secret = utils.GetFlakeUidStr()
+}
+
+func initAuthentication(conf Authentication) {
+	AuthConf = conf
+	if AuthConf.Secret == "secret" {
+		AuthConf.Secret = utils.GetFlakeUidStr()
 	}
 
-	AuthenticationConfig.Session.ExpireTime = configuration.Get("authentication.session.expireTime").Uint()
+	userList := conf.Users
+	// create user list
+	for _, item := range userList {
+		UserList[item.Username] = item
+	}
 }
 
 func initSchema() {
-	filePath := getSchemaPath()
+	filePath := WorkDir + "/conf/schema.json"
 	if schemaContent, err := ioutil.ReadFile(filePath); err != nil {
 		panic(fmt.Sprintf("fail to read configuration: %s", filePath))
 	} else {
diff --git a/api/conf/conf.json b/api/conf/conf.json
deleted file mode 100644
index d00c492..0000000
--- a/api/conf/conf.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "conf": {
-    "syslog": {
-      "host": "127.0.0.1"
-    }
-  },
-  "authentication": {
-    "session": {
-      "secret": "secret",
-      "expireTime": 3600
-    },
-    "user": [
-      {
-        "username": "admin",
-        "password": "admin"
-      },
-      {
-        "username": "user",
-        "password": "user"
-      }
-    ]
-  }
-}
diff --git a/api/conf/conf.yaml b/api/conf/conf.yaml
new file mode 100644
index 0000000..2270e72
--- /dev/null
+++ b/api/conf/conf.yaml
@@ -0,0 +1,41 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+conf:
+  listen:
+    host: 127.0.0.1 # `manager api` listening ip or host name
+    port: 8080 # `manager api` listening port
+  etcd:
+    endpoints: # supports defining multiple etcd host addresses for an etcd cluster
+      - 127.0.0.1:2379
+  log:
+    error_log:
+      level: warn # supports levels, lower to higher: debug, info, warn, error, panic, fatal
+      file_path:
+        logs/error.log # supports relative path, absolute path, standard output
+        # such as: logs/error.log, /tmp/logs/error.log, /dev/stdout, /dev/stderr
+authentication:
+  secret:
+    secret # secret for jwt token generation.
+    # NOTE: Highly recommended to modify this value to protect `manager api`.
+    # if it's default value, when `manager api` start , it will generate a random string to replace it.
+  expire_time: 3600 # jwt token expire time, in second
+  users:
+    - username: admin # username and password for login `manager api`
+      password: admin
+    - username: user
+      password: user
diff --git a/api/conf/conf_preview.json b/api/conf/conf_preview.json
deleted file mode 100644
index eef8683..0000000
--- a/api/conf/conf_preview.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "conf": {
-    "syslog": {
-      "host": "#syslogAddress#"
-    }
-  },
-  "authentication": {
-    "session": {
-      "secret": "secret",
-      "expireTime": 3600
-    },
-    "user": [
-      {
-        "username": "admin",
-        "password": "admin"
-      },
-      {
-        "username": "user",
-        "password": "user"
-      }
-    ]
-  }
-}
diff --git a/api/conf/schema.json b/api/conf/schema.json
index 087cf4c..a684d3c 100644
--- a/api/conf/schema.json
+++ b/api/conf/schema.json
@@ -1 +1,3499 @@
-{"main":{"upstream_hash_header_schema":{"type":"string","pattern":"^[a-zA-Z0-9-_]+$"},"stream_route":{"type":"object","properties":{"plugins":{"type":"object"},"id":{"anyOf":[{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-zA-Z0-9-_]+$"},{"type":"integer","minimum":1}]},"remote_addr":{"type":"string","anyOf":[{"title":"IPv4","type":"string","pattern":"^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"},{"title":"IPv4/CIDR","type":"string","pattern":"^[0-9]{1,3}.[0-9]{1,3}.[0-9]{ [...]
+{
+	"main": {
+		"global_rule": {
+			"additionalProperties": false,
+			"type": "object",
+			"required": ["plugins"],
+			"properties": {
+				"id": {
+					"anyOf": [{
+						"maxLength": 64,
+						"type": "string",
+						"pattern": "^[a-zA-Z0-9-_]+$",
+						"minLength": 1
+					}, {
+						"minimum": 1,
+						"type": "integer"
+					}]
+				},
+				"plugins": {
+					"type": "object"
+				}
+			}
+		},
+		"upstream": {
+			"properties": {
+				"update_time": {
+					"type": "integer"
+				},
+				"retries": {
+					"minimum": 0,
+					"type": "integer"
+				},
+				"k8s_deployment_info": {
+					"type": "object",
+					"properties": {
+						"port": {
+							"minimum": 0,
+							"type": "number"
+						},
+						"deploy_name": {
+							"description": "k8s deployment name",
+							"type": "string"
+						},
+						"service_name": {
+							"description": "k8s service name",
+							"type": "string"
+						},
+						"namespace": {
+							"description": "k8s namespace",
+							"type": "string"
+						},
+						"backend_type": {
+							"enum": ["svc", "pod"],
+							"type": "string",
+							"default": "pod",
+							"description": "k8s service name"
+						}
+					},
+					"anyOf": [{
+						"required": ["namespace", "deploy_name", "port"]
+					}, {
+						"required": ["namespace", "service_name", "port"]
+					}]
+				},
+				"key": {
+					"description": "the key of chash for dynamic load balancing",
+					"type": "string"
+				},
+				"pass_host": {
+					"default": "pass",
+					"type": "string",
+					"description": "mod of host passing",
+					"enum": ["pass", "node", "rewrite"]
+				},
+				"type": {
+					"type": "string",
+					"description": "algorithms of load balancing",
+					"enum": ["chash", "roundrobin", "ewma"]
+				},
+				"timeout": {
+					"type": "object",
+					"required": ["connect", "send", "read"],
+					"properties": {
+						"send": {
+							"minimum": 0,
+							"type": "number"
+						},
+						"read": {
+							"minimum": 0,
+							"type": "number"
+						},
+						"connect": {
+							"minimum": 0,
+							"type": "number"
+						}
+					}
+				},
+				"checks": {
+					"properties": {
+						"passive": {
+							"properties": {
+								"unhealthy": {
+									"properties": {
+										"http_failures": {
+											"minimum": 1,
+											"type": "integer",
+											"default": 5,
+											"maximum": 254
+										},
+										"timeouts": {
+											"minimum": 1,
+											"type": "integer",
+											"default": 7,
+											"maximum": 254
+										},
+										"http_statuses": {
+											"items": {
+												"minimum": 200,
+												"type": "integer",
+												"maximum": 599
+											},
+											"type": "array",
+											"default": [429, 500, 503],
+											"minItems": 1,
+											"uniqueItems": true
+										},
+										"tcp_failures": {
+											"minimum": 1,
+											"type": "integer",
+											"default": 2,
+											"maximum": 254
+										}
+									},
+									"type": "object"
+								},
+								"type": {
+									"type": "string",
+									"default": "http",
+									"enum": ["http", "https", "tcp"]
+								},
+								"healthy": {
+									"properties": {
+										"http_statuses": {
+											"items": {
+												"minimum": 200,
+												"type": "integer",
+												"maximum": 599
+											},
+											"type": "array",
+											"default": [200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, 306, 307, 308],
+											"minItems": 1,
+											"uniqueItems": true
+										},
+										"successes": {
+											"minimum": 1,
+											"type": "integer",
+											"default": 5,
+											"maximum": 254
+										}
+									},
+									"type": "object"
+								}
+							},
+							"type": "object"
+						},
+						"active": {
+							"properties": {
+								"type": {
+									"type": "string",
+									"default": "http",
+									"enum": ["http", "https", "tcp"]
+								},
+								"timeout": {
+									"default": 1,
+									"type": "number"
+								},
+								"req_headers": {
+									"items": {
+										"uniqueItems": true,
+										"type": "string"
+									},
+									"type": "array",
+									"minItems": 1
+								},
+								"http_path": {
+									"default": "/",
+									"type": "string"
+								},
+								"concurrency": {
+									"default": 10,
+									"type": "integer"
+								},
+								"host": {
+									"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+									"type": "string"
+								},
+								"port": {
+									"minimum": 1,
+									"type": "integer",
+									"maximum": 65535
+								},
+								"unhealthy": {
+									"properties": {
+										"timeouts": {
+											"minimum": 1,
+											"type": "integer",
+											"default": 3,
+											"maximum": 254
+										},
+										"http_failures": {
+											"minimum": 1,
+											"type": "integer",
+											"default": 5,
+											"maximum": 254
+										},
+										"interval": {
+											"minimum": 1,
+											"type": "integer",
+											"default": 0
+										},
+										"http_statuses": {
+											"items": {
+												"minimum": 200,
+												"type": "integer",
+												"maximum": 599
+											},
+											"type": "array",
+											"default": [429, 404, 500, 501, 502, 503, 504, 505],
+											"minItems": 1,
+											"uniqueItems": true
+										},
+										"tcp_failures": {
+											"minimum": 1,
+											"type": "integer",
+											"default": 2,
+											"maximum": 254
+										}
+									},
+									"type": "object"
+								},
+								"healthy": {
+									"properties": {
+										"interval": {
+											"minimum": 1,
+											"type": "integer",
+											"default": 0
+										},
+										"http_statuses": {
+											"items": {
+												"minimum": 200,
+												"type": "integer",
+												"maximum": 599
+											},
+											"type": "array",
+											"default": [200, 302],
+											"minItems": 1,
+											"uniqueItems": true
+										},
+										"successes": {
+											"minimum": 1,
+											"type": "integer",
+											"default": 2,
+											"maximum": 254
+										}
+									},
+									"type": "object"
+								},
+								"https_verify_certificate": {
+									"default": true,
+									"type": "boolean"
+								}
+							},
+							"type": "object"
+						}
+					},
+					"type": "object",
+					"additionalProperties": false,
+					"anyOf": [{
+						"required": ["active"]
+					}, {
+						"required": ["active", "passive"]
+					}]
+				},
+				"hash_on": {
+					"type": "string",
+					"default": "vars",
+					"enum": ["vars", "header", "cookie", "consumer"]
+				},
+				"upstream_host": {
+					"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+					"type": "string"
+				},
+				"discovery_type": {
+					"description": "discovery type",
+					"type": "string"
+				},
+				"create_time": {
+					"type": "integer"
+				},
+				"name": {
+					"maxLength": 50,
+					"type": "string"
+				},
+				"id": {
+					"anyOf": [{
+						"maxLength": 64,
+						"type": "string",
+						"pattern": "^[a-zA-Z0-9-_]+$",
+						"minLength": 1
+					}, {
+						"minimum": 1,
+						"type": "integer"
+					}]
+				},
+				"nodes": {
+					"anyOf": [{
+						"minProperties": 1,
+						"type": "object",
+						"patternProperties": {
+							".*": {
+								"minimum": 0,
+								"type": "integer",
+								"description": "weight of node"
+							}
+						}
+					}, {
+						"items": {
+							"type": "object",
+							"required": ["host", "port", "weight"],
+							"properties": {
+								"host": {
+									"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+									"type": "string"
+								},
+								"port": {
+									"minimum": 1,
+									"type": "integer",
+									"description": "port of node"
+								},
+								"metadata": {
+									"description": "metadata of node",
+									"type": "object"
+								},
+								"weight": {
+									"minimum": 0,
+									"type": "integer",
+									"description": "weight of node"
+								}
+							}
+						},
+						"type": "array",
+						"minItems": 1
+					}]
+				},
+				"service_name": {
+					"maxLength": 50,
+					"type": "string"
+				},
+				"desc": {
+					"maxLength": 256,
+					"type": "string"
+				},
+				"labels": {
+					"maxProperties": 16,
+					"type": "object",
+					"description": "key/value pairs to specify attributes",
+					"patternProperties": {
+						".*": {
+							"type": "string",
+							"description": "value of label",
+							"minLength": 1,
+							"pattern": "^[a-zA-Z0-9-_.]+$",
+							"maxLength": 64
+						}
+					}
+				}
+			},
+			"type": "object",
+			"additionalProperties": false,
+			"anyOf": [{
+				"required": ["type", "nodes"]
+			}, {
+				"required": ["type", "k8s_deployment_info"]
+			}, {
+				"required": ["type", "service_name"]
+			}]
+		},
+		"id_schema": {
+			"anyOf": [{
+				"maxLength": 64,
+				"type": "string",
+				"pattern": "^[a-zA-Z0-9-_]+$",
+				"minLength": 1
+			}, {
+				"minimum": 1,
+				"type": "integer"
+			}]
+		},
+		"service": {
+			"type": "object",
+			"additionalProperties": false,
+			"properties": {
+				"script": {
+					"type": "string",
+					"maxLength": 102400,
+					"minLength": 10
+				},
+				"upstream": {
+					"properties": {
+						"update_time": {
+							"type": "integer"
+						},
+						"retries": {
+							"minimum": 0,
+							"type": "integer"
+						},
+						"k8s_deployment_info": {
+							"type": "object",
+							"properties": {
+								"port": {
+									"minimum": 0,
+									"type": "number"
+								},
+								"deploy_name": {
+									"description": "k8s deployment name",
+									"type": "string"
+								},
+								"service_name": {
+									"description": "k8s service name",
+									"type": "string"
+								},
+								"namespace": {
+									"description": "k8s namespace",
+									"type": "string"
+								},
+								"backend_type": {
+									"enum": ["svc", "pod"],
+									"type": "string",
+									"default": "pod",
+									"description": "k8s service name"
+								}
+							},
+							"anyOf": [{
+								"required": ["namespace", "deploy_name", "port"]
+							}, {
+								"required": ["namespace", "service_name", "port"]
+							}]
+						},
+						"key": {
+							"description": "the key of chash for dynamic load balancing",
+							"type": "string"
+						},
+						"pass_host": {
+							"default": "pass",
+							"type": "string",
+							"description": "mod of host passing",
+							"enum": ["pass", "node", "rewrite"]
+						},
+						"type": {
+							"type": "string",
+							"description": "algorithms of load balancing",
+							"enum": ["chash", "roundrobin", "ewma"]
+						},
+						"timeout": {
+							"type": "object",
+							"required": ["connect", "send", "read"],
+							"properties": {
+								"send": {
+									"minimum": 0,
+									"type": "number"
+								},
+								"read": {
+									"minimum": 0,
+									"type": "number"
+								},
+								"connect": {
+									"minimum": 0,
+									"type": "number"
+								}
+							}
+						},
+						"checks": {
+							"properties": {
+								"passive": {
+									"properties": {
+										"unhealthy": {
+											"properties": {
+												"http_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 5,
+													"maximum": 254
+												},
+												"timeouts": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 7,
+													"maximum": 254
+												},
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [429, 500, 503],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"tcp_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 2,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										},
+										"type": {
+											"type": "string",
+											"default": "http",
+											"enum": ["http", "https", "tcp"]
+										},
+										"healthy": {
+											"properties": {
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, 306, 307, 308],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"successes": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 5,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										}
+									},
+									"type": "object"
+								},
+								"active": {
+									"properties": {
+										"type": {
+											"type": "string",
+											"default": "http",
+											"enum": ["http", "https", "tcp"]
+										},
+										"timeout": {
+											"default": 1,
+											"type": "number"
+										},
+										"req_headers": {
+											"items": {
+												"uniqueItems": true,
+												"type": "string"
+											},
+											"type": "array",
+											"minItems": 1
+										},
+										"http_path": {
+											"default": "/",
+											"type": "string"
+										},
+										"concurrency": {
+											"default": 10,
+											"type": "integer"
+										},
+										"host": {
+											"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+											"type": "string"
+										},
+										"port": {
+											"minimum": 1,
+											"type": "integer",
+											"maximum": 65535
+										},
+										"unhealthy": {
+											"properties": {
+												"timeouts": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 3,
+													"maximum": 254
+												},
+												"http_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 5,
+													"maximum": 254
+												},
+												"interval": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 0
+												},
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [429, 404, 500, 501, 502, 503, 504, 505],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"tcp_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 2,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										},
+										"healthy": {
+											"properties": {
+												"interval": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 0
+												},
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [200, 302],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"successes": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 2,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										},
+										"https_verify_certificate": {
+											"default": true,
+											"type": "boolean"
+										}
+									},
+									"type": "object"
+								}
+							},
+							"type": "object",
+							"additionalProperties": false,
+							"anyOf": [{
+								"required": ["active"]
+							}, {
+								"required": ["active", "passive"]
+							}]
+						},
+						"hash_on": {
+							"type": "string",
+							"default": "vars",
+							"enum": ["vars", "header", "cookie", "consumer"]
+						},
+						"upstream_host": {
+							"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+							"type": "string"
+						},
+						"discovery_type": {
+							"description": "discovery type",
+							"type": "string"
+						},
+						"create_time": {
+							"type": "integer"
+						},
+						"name": {
+							"maxLength": 50,
+							"type": "string"
+						},
+						"id": {
+							"anyOf": [{
+								"maxLength": 64,
+								"type": "string",
+								"pattern": "^[a-zA-Z0-9-_]+$",
+								"minLength": 1
+							}, {
+								"minimum": 1,
+								"type": "integer"
+							}]
+						},
+						"nodes": {
+							"anyOf": [{
+								"minProperties": 1,
+								"type": "object",
+								"patternProperties": {
+									".*": {
+										"minimum": 0,
+										"type": "integer",
+										"description": "weight of node"
+									}
+								}
+							}, {
+								"items": {
+									"type": "object",
+									"required": ["host", "port", "weight"],
+									"properties": {
+										"host": {
+											"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+											"type": "string"
+										},
+										"port": {
+											"minimum": 1,
+											"type": "integer",
+											"description": "port of node"
+										},
+										"metadata": {
+											"description": "metadata of node",
+											"type": "object"
+										},
+										"weight": {
+											"minimum": 0,
+											"type": "integer",
+											"description": "weight of node"
+										}
+									}
+								},
+								"type": "array",
+								"minItems": 1
+							}]
+						},
+						"service_name": {
+							"maxLength": 50,
+							"type": "string"
+						},
+						"desc": {
+							"maxLength": 256,
+							"type": "string"
+						},
+						"labels": {
+							"maxProperties": 16,
+							"type": "object",
+							"description": "key/value pairs to specify attributes",
+							"patternProperties": {
+								".*": {
+									"type": "string",
+									"description": "value of label",
+									"minLength": 1,
+									"pattern": "^[a-zA-Z0-9-_.]+$",
+									"maxLength": 64
+								}
+							}
+						}
+					},
+					"type": "object",
+					"additionalProperties": false,
+					"anyOf": [{
+						"required": ["type", "nodes"]
+					}, {
+						"required": ["type", "k8s_deployment_info"]
+					}, {
+						"required": ["type", "service_name"]
+					}]
+				},
+				"id": {
+					"anyOf": [{
+						"maxLength": 64,
+						"type": "string",
+						"pattern": "^[a-zA-Z0-9-_]+$",
+						"minLength": 1
+					}, {
+						"minimum": 1,
+						"type": "integer"
+					}]
+				},
+				"desc": {
+					"maxLength": 256,
+					"type": "string"
+				},
+				"create_time": {
+					"type": "integer"
+				},
+				"name": {
+					"maxLength": 50,
+					"type": "string"
+				},
+				"upstream_id": {
+					"anyOf": [{
+						"maxLength": 64,
+						"type": "string",
+						"pattern": "^[a-zA-Z0-9-_]+$",
+						"minLength": 1
+					}, {
+						"minimum": 1,
+						"type": "integer"
+					}]
+				},
+				"plugins": {
+					"type": "object"
+				},
+				"enable_websocket": {
+					"description": "enable websocket for request",
+					"type": "boolean"
+				},
+				"update_time": {
+					"type": "integer"
+				},
+				"labels": {
+					"maxProperties": 16,
+					"type": "object",
+					"description": "key/value pairs to specify attributes",
+					"patternProperties": {
+						".*": {
+							"type": "string",
+							"description": "value of label",
+							"minLength": 1,
+							"pattern": "^[a-zA-Z0-9-_.]+$",
+							"maxLength": 64
+						}
+					}
+				}
+			}
+		},
+		"ip_def": [{
+			"type": "string",
+			"title": "IPv4",
+			"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"
+		}, {
+			"type": "string",
+			"title": "IPv4/CIDR",
+			"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}$"
+		}, {
+			"type": "string",
+			"title": "IPv6",
+			"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?$"
+		}, {
+			"type": "string",
+			"title": "IPv6/CIDR",
+			"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$"
+		}],
+		"host_def": {
+			"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+			"type": "string"
+		},
+		"ssl": {
+			"oneOf": [{
+				"required": ["sni", "key", "cert"]
+			}, {
+				"required": ["snis", "key", "cert"]
+			}],
+			"type": "object",
+			"additionalProperties": false,
+			"properties": {
+				"update_time": {
+					"type": "integer"
+				},
+				"create_time": {
+					"type": "integer"
+				},
+				"id": {
+					"anyOf": [{
+						"maxLength": 64,
+						"type": "string",
+						"pattern": "^[a-zA-Z0-9-_]+$",
+						"minLength": 1
+					}, {
+						"minimum": 1,
+						"type": "integer"
+					}]
+				},
+				"status": {
+					"default": 1,
+					"type": "integer",
+					"description": "ssl status, 1 to enable, 0 to disable",
+					"enum": [1, 0]
+				},
+				"cert": {
+					"type": "string",
+					"maxLength": 65536,
+					"minLength": 128
+				},
+				"sni": {
+					"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+					"type": "string"
+				},
+				"validity_end": {
+					"type": "integer"
+				},
+				"validity_start": {
+					"type": "integer"
+				},
+				"key": {
+					"type": "string",
+					"maxLength": 65536,
+					"minLength": 128
+				},
+				"exptime": {
+					"minimum": 1588262400,
+					"type": "integer"
+				},
+				"keys": {
+					"items": {
+						"type": "string",
+						"maxLength": 65536,
+						"minLength": 128
+					},
+					"type": "array"
+				},
+				"certs": {
+					"items": {
+						"type": "string",
+						"maxLength": 65536,
+						"minLength": 128
+					},
+					"type": "array"
+				},
+				"snis": {
+					"items": {
+						"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+						"type": "string"
+					},
+					"type": "array"
+				},
+				"labels": {
+					"maxProperties": 16,
+					"type": "object",
+					"description": "key/value pairs to specify attributes",
+					"patternProperties": {
+						".*": {
+							"type": "string",
+							"description": "value of label",
+							"minLength": 1,
+							"pattern": "^[a-zA-Z0-9-_.]+$",
+							"maxLength": 64
+						}
+					}
+				}
+			}
+		},
+		"proto": {
+			"additionalProperties": false,
+			"type": "object",
+			"required": ["content"],
+			"properties": {
+				"content": {
+					"type": "string",
+					"maxLength": 1048576,
+					"minLength": 1
+				}
+			}
+		},
+		"route": {
+			"type": "object",
+			"additionalProperties": false,
+			"properties": {
+				"service_id": {
+					"anyOf": [{
+						"maxLength": 64,
+						"type": "string",
+						"pattern": "^[a-zA-Z0-9-_]+$",
+						"minLength": 1
+					}, {
+						"minimum": 1,
+						"type": "integer"
+					}]
+				},
+				"update_time": {
+					"type": "integer"
+				},
+				"uris": {
+					"items": {
+						"description": "HTTP uri",
+						"type": "string"
+					},
+					"type": "array",
+					"uniqueItems": true
+				},
+				"hosts": {
+					"items": {
+						"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+						"type": "string"
+					},
+					"type": "array",
+					"uniqueItems": true
+				},
+				"remote_addrs": {
+					"items": {
+						"type": "string",
+						"description": "client IP",
+						"anyOf": [{
+							"type": "string",
+							"title": "IPv4",
+							"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"
+						}, {
+							"type": "string",
+							"title": "IPv4/CIDR",
+							"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}$"
+						}, {
+							"type": "string",
+							"title": "IPv6",
+							"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?$"
+						}, {
+							"type": "string",
+							"title": "IPv6/CIDR",
+							"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$"
+						}]
+					},
+					"type": "array",
+					"uniqueItems": true
+				},
+				"host": {
+					"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+					"type": "string"
+				},
+				"remote_addr": {
+					"type": "string",
+					"description": "client IP",
+					"anyOf": [{
+						"type": "string",
+						"title": "IPv4",
+						"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"
+					}, {
+						"type": "string",
+						"title": "IPv4/CIDR",
+						"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}$"
+					}, {
+						"type": "string",
+						"title": "IPv6",
+						"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?$"
+					}, {
+						"type": "string",
+						"title": "IPv6/CIDR",
+						"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$"
+					}]
+				},
+				"upstream_id": {
+					"anyOf": [{
+						"maxLength": 64,
+						"type": "string",
+						"pattern": "^[a-zA-Z0-9-_]+$",
+						"minLength": 1
+					}, {
+						"minimum": 1,
+						"type": "integer"
+					}]
+				},
+				"plugins": {
+					"type": "object"
+				},
+				"priority": {
+					"default": 0,
+					"type": "integer"
+				},
+				"uri": {
+					"type": "string",
+					"maxLength": 4096,
+					"minLength": 1
+				},
+				"methods": {
+					"items": {
+						"type": "string",
+						"description": "HTTP method",
+						"enum": ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE"]
+					},
+					"type": "array",
+					"uniqueItems": true
+				},
+				"script": {
+					"type": "string",
+					"maxLength": 102400,
+					"minLength": 10
+				},
+				"id": {
+					"anyOf": [{
+						"maxLength": 64,
+						"type": "string",
+						"pattern": "^[a-zA-Z0-9-_]+$",
+						"minLength": 1
+					}, {
+						"minimum": 1,
+						"type": "integer"
+					}]
+				},
+				"desc": {
+					"maxLength": 256,
+					"type": "string"
+				},
+				"enable_websocket": {
+					"description": "enable websocket for request",
+					"type": "boolean"
+				},
+				"create_time": {
+					"type": "integer"
+				},
+				"name": {
+					"maxLength": 50,
+					"type": "string"
+				},
+				"service_protocol": {
+					"enum": ["grpc", "http"]
+				},
+				"vars": {
+					"items": {
+						"items": {
+							"maxItems": 3,
+							"minItems": 2,
+							"anyOf": [{
+								"type": "string"
+							}, {
+								"type": "number"
+							}]
+						},
+						"type": "array",
+						"description": "Nginx builtin variable name and value"
+					},
+					"type": "array"
+				},
+				"upstream": {
+					"properties": {
+						"update_time": {
+							"type": "integer"
+						},
+						"retries": {
+							"minimum": 0,
+							"type": "integer"
+						},
+						"k8s_deployment_info": {
+							"type": "object",
+							"properties": {
+								"port": {
+									"minimum": 0,
+									"type": "number"
+								},
+								"deploy_name": {
+									"description": "k8s deployment name",
+									"type": "string"
+								},
+								"service_name": {
+									"description": "k8s service name",
+									"type": "string"
+								},
+								"namespace": {
+									"description": "k8s namespace",
+									"type": "string"
+								},
+								"backend_type": {
+									"enum": ["svc", "pod"],
+									"type": "string",
+									"default": "pod",
+									"description": "k8s service name"
+								}
+							},
+							"anyOf": [{
+								"required": ["namespace", "deploy_name", "port"]
+							}, {
+								"required": ["namespace", "service_name", "port"]
+							}]
+						},
+						"key": {
+							"description": "the key of chash for dynamic load balancing",
+							"type": "string"
+						},
+						"pass_host": {
+							"default": "pass",
+							"type": "string",
+							"description": "mod of host passing",
+							"enum": ["pass", "node", "rewrite"]
+						},
+						"type": {
+							"type": "string",
+							"description": "algorithms of load balancing",
+							"enum": ["chash", "roundrobin", "ewma"]
+						},
+						"timeout": {
+							"type": "object",
+							"required": ["connect", "send", "read"],
+							"properties": {
+								"send": {
+									"minimum": 0,
+									"type": "number"
+								},
+								"read": {
+									"minimum": 0,
+									"type": "number"
+								},
+								"connect": {
+									"minimum": 0,
+									"type": "number"
+								}
+							}
+						},
+						"checks": {
+							"properties": {
+								"passive": {
+									"properties": {
+										"unhealthy": {
+											"properties": {
+												"http_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 5,
+													"maximum": 254
+												},
+												"timeouts": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 7,
+													"maximum": 254
+												},
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [429, 500, 503],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"tcp_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 2,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										},
+										"type": {
+											"type": "string",
+											"default": "http",
+											"enum": ["http", "https", "tcp"]
+										},
+										"healthy": {
+											"properties": {
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, 306, 307, 308],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"successes": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 5,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										}
+									},
+									"type": "object"
+								},
+								"active": {
+									"properties": {
+										"type": {
+											"type": "string",
+											"default": "http",
+											"enum": ["http", "https", "tcp"]
+										},
+										"timeout": {
+											"default": 1,
+											"type": "number"
+										},
+										"req_headers": {
+											"items": {
+												"uniqueItems": true,
+												"type": "string"
+											},
+											"type": "array",
+											"minItems": 1
+										},
+										"http_path": {
+											"default": "/",
+											"type": "string"
+										},
+										"concurrency": {
+											"default": 10,
+											"type": "integer"
+										},
+										"host": {
+											"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+											"type": "string"
+										},
+										"port": {
+											"minimum": 1,
+											"type": "integer",
+											"maximum": 65535
+										},
+										"unhealthy": {
+											"properties": {
+												"timeouts": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 3,
+													"maximum": 254
+												},
+												"http_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 5,
+													"maximum": 254
+												},
+												"interval": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 0
+												},
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [429, 404, 500, 501, 502, 503, 504, 505],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"tcp_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 2,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										},
+										"healthy": {
+											"properties": {
+												"interval": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 0
+												},
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [200, 302],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"successes": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 2,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										},
+										"https_verify_certificate": {
+											"default": true,
+											"type": "boolean"
+										}
+									},
+									"type": "object"
+								}
+							},
+							"type": "object",
+							"additionalProperties": false,
+							"anyOf": [{
+								"required": ["active"]
+							}, {
+								"required": ["active", "passive"]
+							}]
+						},
+						"hash_on": {
+							"type": "string",
+							"default": "vars",
+							"enum": ["vars", "header", "cookie", "consumer"]
+						},
+						"upstream_host": {
+							"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+							"type": "string"
+						},
+						"discovery_type": {
+							"description": "discovery type",
+							"type": "string"
+						},
+						"create_time": {
+							"type": "integer"
+						},
+						"name": {
+							"maxLength": 50,
+							"type": "string"
+						},
+						"id": {
+							"anyOf": [{
+								"maxLength": 64,
+								"type": "string",
+								"pattern": "^[a-zA-Z0-9-_]+$",
+								"minLength": 1
+							}, {
+								"minimum": 1,
+								"type": "integer"
+							}]
+						},
+						"nodes": {
+							"anyOf": [{
+								"minProperties": 1,
+								"type": "object",
+								"patternProperties": {
+									".*": {
+										"minimum": 0,
+										"type": "integer",
+										"description": "weight of node"
+									}
+								}
+							}, {
+								"items": {
+									"type": "object",
+									"required": ["host", "port", "weight"],
+									"properties": {
+										"host": {
+											"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+											"type": "string"
+										},
+										"port": {
+											"minimum": 1,
+											"type": "integer",
+											"description": "port of node"
+										},
+										"metadata": {
+											"description": "metadata of node",
+											"type": "object"
+										},
+										"weight": {
+											"minimum": 0,
+											"type": "integer",
+											"description": "weight of node"
+										}
+									}
+								},
+								"type": "array",
+								"minItems": 1
+							}]
+						},
+						"service_name": {
+							"maxLength": 50,
+							"type": "string"
+						},
+						"desc": {
+							"maxLength": 256,
+							"type": "string"
+						},
+						"labels": {
+							"maxProperties": 16,
+							"type": "object",
+							"description": "key/value pairs to specify attributes",
+							"patternProperties": {
+								".*": {
+									"type": "string",
+									"description": "value of label",
+									"minLength": 1,
+									"pattern": "^[a-zA-Z0-9-_.]+$",
+									"maxLength": 64
+								}
+							}
+						}
+					},
+					"type": "object",
+					"additionalProperties": false,
+					"anyOf": [{
+						"required": ["type", "nodes"]
+					}, {
+						"required": ["type", "k8s_deployment_info"]
+					}, {
+						"required": ["type", "service_name"]
+					}]
+				},
+				"filter_func": {
+					"type": "string",
+					"pattern": "^function",
+					"minLength": 10
+				},
+				"labels": {
+					"maxProperties": 16,
+					"type": "object",
+					"description": "key/value pairs to specify attributes",
+					"patternProperties": {
+						".*": {
+							"type": "string",
+							"description": "value of label",
+							"minLength": 1,
+							"pattern": "^[a-zA-Z0-9-_.]+$",
+							"maxLength": 64
+						}
+					}
+				}
+			},
+			"not": {
+				"anyOf": [{
+					"required": ["script", "plugins"]
+				}]
+			},
+			"anyOf": [{
+				"required": ["plugins", "uri"]
+			}, {
+				"required": ["upstream", "uri"]
+			}, {
+				"required": ["upstream_id", "uri"]
+			}, {
+				"required": ["service_id", "uri"]
+			}, {
+				"required": ["plugins", "uris"]
+			}, {
+				"required": ["upstream", "uris"]
+			}, {
+				"required": ["upstream_id", "uris"]
+			}, {
+				"required": ["service_id", "uris"]
+			}, {
+				"required": ["script", "uri"]
+			}, {
+				"required": ["script", "uris"]
+			}]
+		},
+		"upstream_hash_header_schema": {
+			"pattern": "^[a-zA-Z0-9-_]+$",
+			"type": "string"
+		},
+		"upstream_hash_vars_schema": {
+			"pattern": "^((uri|server_name|server_addr|request_uri|remote_port|remote_addr|query_string|host|hostname)|arg_[0-9a-zA-z_-]+)$",
+			"type": "string"
+		},
+		"version": 0.5,
+		"stream_route": {
+			"properties": {
+				"remote_addr": {
+					"type": "string",
+					"description": "client IP",
+					"anyOf": [{
+						"type": "string",
+						"title": "IPv4",
+						"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"
+					}, {
+						"type": "string",
+						"title": "IPv4/CIDR",
+						"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}$"
+					}, {
+						"type": "string",
+						"title": "IPv6",
+						"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?$"
+					}, {
+						"type": "string",
+						"title": "IPv6/CIDR",
+						"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$"
+					}]
+				},
+				"plugins": {
+					"type": "object"
+				},
+				"server_addr": {
+					"type": "string",
+					"description": "server IP",
+					"anyOf": [{
+						"type": "string",
+						"title": "IPv4",
+						"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"
+					}, {
+						"type": "string",
+						"title": "IPv4/CIDR",
+						"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}$"
+					}, {
+						"type": "string",
+						"title": "IPv6",
+						"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?$"
+					}, {
+						"type": "string",
+						"title": "IPv6/CIDR",
+						"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$"
+					}]
+				},
+				"id": {
+					"anyOf": [{
+						"maxLength": 64,
+						"type": "string",
+						"pattern": "^[a-zA-Z0-9-_]+$",
+						"minLength": 1
+					}, {
+						"minimum": 1,
+						"type": "integer"
+					}]
+				},
+				"upstream_id": {
+					"anyOf": [{
+						"maxLength": 64,
+						"type": "string",
+						"pattern": "^[a-zA-Z0-9-_]+$",
+						"minLength": 1
+					}, {
+						"minimum": 1,
+						"type": "integer"
+					}]
+				},
+				"server_port": {
+					"description": "server port",
+					"type": "integer"
+				},
+				"upstream": {
+					"properties": {
+						"update_time": {
+							"type": "integer"
+						},
+						"retries": {
+							"minimum": 0,
+							"type": "integer"
+						},
+						"k8s_deployment_info": {
+							"type": "object",
+							"properties": {
+								"port": {
+									"minimum": 0,
+									"type": "number"
+								},
+								"deploy_name": {
+									"description": "k8s deployment name",
+									"type": "string"
+								},
+								"service_name": {
+									"description": "k8s service name",
+									"type": "string"
+								},
+								"namespace": {
+									"description": "k8s namespace",
+									"type": "string"
+								},
+								"backend_type": {
+									"enum": ["svc", "pod"],
+									"type": "string",
+									"default": "pod",
+									"description": "k8s service name"
+								}
+							},
+							"anyOf": [{
+								"required": ["namespace", "deploy_name", "port"]
+							}, {
+								"required": ["namespace", "service_name", "port"]
+							}]
+						},
+						"key": {
+							"description": "the key of chash for dynamic load balancing",
+							"type": "string"
+						},
+						"pass_host": {
+							"default": "pass",
+							"type": "string",
+							"description": "mod of host passing",
+							"enum": ["pass", "node", "rewrite"]
+						},
+						"type": {
+							"type": "string",
+							"description": "algorithms of load balancing",
+							"enum": ["chash", "roundrobin", "ewma"]
+						},
+						"timeout": {
+							"type": "object",
+							"required": ["connect", "send", "read"],
+							"properties": {
+								"send": {
+									"minimum": 0,
+									"type": "number"
+								},
+								"read": {
+									"minimum": 0,
+									"type": "number"
+								},
+								"connect": {
+									"minimum": 0,
+									"type": "number"
+								}
+							}
+						},
+						"checks": {
+							"properties": {
+								"passive": {
+									"properties": {
+										"unhealthy": {
+											"properties": {
+												"http_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 5,
+													"maximum": 254
+												},
+												"timeouts": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 7,
+													"maximum": 254
+												},
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [429, 500, 503],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"tcp_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 2,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										},
+										"type": {
+											"type": "string",
+											"default": "http",
+											"enum": ["http", "https", "tcp"]
+										},
+										"healthy": {
+											"properties": {
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, 306, 307, 308],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"successes": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 5,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										}
+									},
+									"type": "object"
+								},
+								"active": {
+									"properties": {
+										"type": {
+											"type": "string",
+											"default": "http",
+											"enum": ["http", "https", "tcp"]
+										},
+										"timeout": {
+											"default": 1,
+											"type": "number"
+										},
+										"req_headers": {
+											"items": {
+												"uniqueItems": true,
+												"type": "string"
+											},
+											"type": "array",
+											"minItems": 1
+										},
+										"http_path": {
+											"default": "/",
+											"type": "string"
+										},
+										"concurrency": {
+											"default": 10,
+											"type": "integer"
+										},
+										"host": {
+											"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+											"type": "string"
+										},
+										"port": {
+											"minimum": 1,
+											"type": "integer",
+											"maximum": 65535
+										},
+										"unhealthy": {
+											"properties": {
+												"timeouts": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 3,
+													"maximum": 254
+												},
+												"http_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 5,
+													"maximum": 254
+												},
+												"interval": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 0
+												},
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [429, 404, 500, 501, 502, 503, 504, 505],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"tcp_failures": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 2,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										},
+										"healthy": {
+											"properties": {
+												"interval": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 0
+												},
+												"http_statuses": {
+													"items": {
+														"minimum": 200,
+														"type": "integer",
+														"maximum": 599
+													},
+													"type": "array",
+													"default": [200, 302],
+													"minItems": 1,
+													"uniqueItems": true
+												},
+												"successes": {
+													"minimum": 1,
+													"type": "integer",
+													"default": 2,
+													"maximum": 254
+												}
+											},
+											"type": "object"
+										},
+										"https_verify_certificate": {
+											"default": true,
+											"type": "boolean"
+										}
+									},
+									"type": "object"
+								}
+							},
+							"type": "object",
+							"additionalProperties": false,
+							"anyOf": [{
+								"required": ["active"]
+							}, {
+								"required": ["active", "passive"]
+							}]
+						},
+						"hash_on": {
+							"type": "string",
+							"default": "vars",
+							"enum": ["vars", "header", "cookie", "consumer"]
+						},
+						"upstream_host": {
+							"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+							"type": "string"
+						},
+						"discovery_type": {
+							"description": "discovery type",
+							"type": "string"
+						},
+						"create_time": {
+							"type": "integer"
+						},
+						"name": {
+							"maxLength": 50,
+							"type": "string"
+						},
+						"id": {
+							"anyOf": [{
+								"maxLength": 64,
+								"type": "string",
+								"pattern": "^[a-zA-Z0-9-_]+$",
+								"minLength": 1
+							}, {
+								"minimum": 1,
+								"type": "integer"
+							}]
+						},
+						"nodes": {
+							"anyOf": [{
+								"minProperties": 1,
+								"type": "object",
+								"patternProperties": {
+									".*": {
+										"minimum": 0,
+										"type": "integer",
+										"description": "weight of node"
+									}
+								}
+							}, {
+								"items": {
+									"type": "object",
+									"required": ["host", "port", "weight"],
+									"properties": {
+										"host": {
+											"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+											"type": "string"
+										},
+										"port": {
+											"minimum": 1,
+											"type": "integer",
+											"description": "port of node"
+										},
+										"metadata": {
+											"description": "metadata of node",
+											"type": "object"
+										},
+										"weight": {
+											"minimum": 0,
+											"type": "integer",
+											"description": "weight of node"
+										}
+									}
+								},
+								"type": "array",
+								"minItems": 1
+							}]
+						},
+						"service_name": {
+							"maxLength": 50,
+							"type": "string"
+						},
+						"desc": {
+							"maxLength": 256,
+							"type": "string"
+						},
+						"labels": {
+							"maxProperties": 16,
+							"type": "object",
+							"description": "key/value pairs to specify attributes",
+							"patternProperties": {
+								".*": {
+									"type": "string",
+									"description": "value of label",
+									"minLength": 1,
+									"pattern": "^[a-zA-Z0-9-_.]+$",
+									"maxLength": 64
+								}
+							}
+						}
+					},
+					"type": "object",
+					"additionalProperties": false,
+					"anyOf": [{
+						"required": ["type", "nodes"]
+					}, {
+						"required": ["type", "k8s_deployment_info"]
+					}, {
+						"required": ["type", "service_name"]
+					}]
+				}
+			},
+			"type": "object"
+		},
+		"plugin_disable_schema": {
+			"disable": {
+				"type": "boolean"
+			}
+		},
+		"plugins": {
+			"items": {
+				"type": "object",
+				"required": ["name"],
+				"properties": {
+					"stream": {
+						"type": "boolean"
+					},
+					"name": {
+						"minLength": 1,
+						"type": "string"
+					},
+					"additionalProperties": false
+				}
+			},
+			"type": "array"
+		},
+		"label_value_def": {
+			"type": "string",
+			"description": "value of label",
+			"minLength": 1,
+			"pattern": "^[a-zA-Z0-9-_.]+$",
+			"maxLength": 64
+		},
+		"consumer": {
+			"additionalProperties": false,
+			"type": "object",
+			"required": ["username"],
+			"properties": {
+				"username": {
+					"maxLength": 32,
+					"type": "string",
+					"pattern": "^[a-zA-Z0-9_]+$",
+					"minLength": 1
+				},
+				"create_time": {
+					"type": "integer"
+				},
+				"update_time": {
+					"type": "integer"
+				},
+				"id": {
+					"anyOf": [{
+						"maxLength": 64,
+						"type": "string",
+						"pattern": "^[a-zA-Z0-9-_]+$",
+						"minLength": 1
+					}, {
+						"minimum": 1,
+						"type": "integer"
+					}]
+				},
+				"desc": {
+					"maxLength": 256,
+					"type": "string"
+				},
+				"plugins": {
+					"type": "object"
+				},
+				"labels": {
+					"maxProperties": 16,
+					"type": "object",
+					"description": "key/value pairs to specify attributes",
+					"patternProperties": {
+						".*": {
+							"type": "string",
+							"description": "value of label",
+							"minLength": 1,
+							"pattern": "^[a-zA-Z0-9-_.]+$",
+							"maxLength": 64
+						}
+					}
+				}
+			}
+		}
+	},
+	"plugins": {
+		"limit-req": {
+			"schema": {
+				"type": "object",
+				"required": ["rate", "burst", "key"],
+				"properties": {
+					"rejected_code": {
+						"minimum": 200,
+						"type": "integer",
+						"default": 503
+					},
+					"key": {
+						"enum": ["remote_addr", "server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name"],
+						"type": "string"
+					},
+					"rate": {
+						"minimum": 0,
+						"type": "number"
+					},
+					"burst": {
+						"minimum": 0,
+						"type": "number"
+					}
+				}
+			}
+		},
+		"zipkin": {
+			"schema": {
+				"type": "object",
+				"required": ["endpoint", "sample_ratio"],
+				"properties": {
+					"endpoint": {
+						"type": "string"
+					},
+					"service_name": {
+						"type": "string",
+						"description": "service name for zipkin reporter",
+						"default": "APISIX"
+					},
+					"sample_ratio": {
+						"minimum": 1e-05,
+						"type": "number",
+						"maximum": 1
+					},
+					"server_addr": {
+						"type": "string",
+						"description": "default is $server_addr, you can speific your external ip address",
+						"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"
+					}
+				}
+			}
+		},
+		"jwt-auth": {
+			"schema": {
+				"type": "object",
+				"additionalProperties": false,
+				"properties": {}
+			},
+			"consumer_schema": {
+				"required": ["key"],
+				"type": "object",
+				"additionalProperties": false,
+				"properties": {
+					"public_key": {
+						"type": "string"
+					},
+					"key": {
+						"type": "string"
+					},
+					"exp": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 86400
+					},
+					"base64_secret": {
+						"default": false,
+						"type": "boolean"
+					},
+					"private_key": {
+						"type": "string"
+					},
+					"algorithm": {
+						"type": "string",
+						"default": "HS256",
+						"enum": ["HS256", "HS512", "RS256"]
+					},
+					"secret": {
+						"type": "string"
+					}
+				}
+			}
+		},
+		"request-id": {
+			"schema": {
+				"properties": {
+					"header_name": {
+						"default": "X-Request-Id",
+						"type": "string"
+					},
+					"include_in_response": {
+						"default": true,
+						"type": "boolean"
+					}
+				},
+				"type": "object"
+			}
+		},
+		"proxy-rewrite": {
+			"schema": {
+				"minProperties": 1,
+				"type": "object",
+				"additionalProperties": false,
+				"properties": {
+					"host": {
+						"type": "string",
+						"description": "new host for upstream",
+						"pattern": "^[0-9a-zA-Z-.]+$"
+					},
+					"regex_uri": {
+						"items": {
+							"description": "regex uri",
+							"type": "string"
+						},
+						"type": "array",
+						"description": "new uri that substitute from client uri for upstream, lower priority than uri property",
+						"maxItems": 2,
+						"minItems": 2
+					},
+					"scheme": {
+						"type": "string",
+						"description": "new scheme for upstream",
+						"enum": ["http", "https"]
+					},
+					"uri": {
+						"type": "string",
+						"description": "new uri for upstream",
+						"pattern": "^\\/.*",
+						"maxLength": 4096,
+						"minLength": 1
+					},
+					"headers": {
+						"minProperties": 1,
+						"type": "object",
+						"description": "new headers for request"
+					}
+				}
+			}
+		},
+		"serverless-post-function": {
+			"schema": {
+				"type": "object",
+				"required": ["functions"],
+				"properties": {
+					"phase": {
+						"enum": ["rewrite", "access", "header_filter", "body_filter", "log", "balancer"],
+						"type": "string"
+					},
+					"functions": {
+						"items": {
+							"type": "string"
+						},
+						"type": "array",
+						"minItems": 1
+					}
+				}
+			}
+		},
+		"ip-restriction": {
+			"schema": {
+				"oneOf": [{
+					"additionalProperties": false,
+					"properties": {
+						"whitelist": {
+							"items": {
+								"anyOf": [{
+									"type": "string",
+									"title": "IPv4",
+									"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"
+								}, {
+									"type": "string",
+									"title": "IPv4/CIDR",
+									"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}$"
+								}, {
+									"type": "string",
+									"title": "IPv6",
+									"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?$"
+								}, {
+									"type": "string",
+									"title": "IPv6/CIDR",
+									"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$"
+								}]
+							},
+							"type": "array",
+							"minItems": 1
+						}
+					},
+					"title": "whitelist",
+					"required": ["whitelist"]
+				}, {
+					"additionalProperties": false,
+					"properties": {
+						"blacklist": {
+							"items": {
+								"anyOf": [{
+									"type": "string",
+									"title": "IPv4",
+									"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"
+								}, {
+									"type": "string",
+									"title": "IPv4/CIDR",
+									"pattern": "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/[0-9]{1,2}$"
+								}, {
+									"type": "string",
+									"title": "IPv6",
+									"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?$"
+								}, {
+									"type": "string",
+									"title": "IPv6/CIDR",
+									"pattern": "^([a-fA-F0-9]{0,4}:){0,8}(:[a-fA-F0-9]{0,4}){0,8}([a-fA-F0-9]{0,4})?/[0-9]{1,3}$"
+								}]
+							},
+							"type": "array",
+							"minItems": 1
+						}
+					},
+					"title": "blacklist",
+					"required": ["blacklist"]
+				}],
+				"type": "object"
+			}
+		},
+		"tcp-logger": {
+			"schema": {
+				"type": "object",
+				"required": ["host", "port"],
+				"properties": {
+					"tls": {
+						"default": false,
+						"type": "boolean"
+					},
+					"timeout": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 1000
+					},
+					"max_retry_count": {
+						"minimum": 0,
+						"type": "integer",
+						"default": 0
+					},
+					"include_req_body": {
+						"default": false,
+						"type": "boolean"
+					},
+					"host": {
+						"type": "string"
+					},
+					"port": {
+						"minimum": 0,
+						"type": "integer"
+					},
+					"name": {
+						"default": "tcp logger",
+						"type": "string"
+					},
+					"batch_max_size": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 1000
+					},
+					"buffer_duration": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 60
+					},
+					"retry_delay": {
+						"minimum": 0,
+						"type": "integer",
+						"default": 1
+					},
+					"inactive_timeout": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 5
+					},
+					"tls_options": {
+						"type": "string"
+					}
+				}
+			}
+		},
+		"cors": {
+			"schema": {
+				"properties": {
+					"allow_credential": {
+						"type": "boolean",
+						"description": "allow client append crendential. according to CORS specification,if you set this option to 'true', you can not use '*' for other options.",
+						"default": false
+					},
+					"allow_headers": {
+						"type": "string",
+						"description": "you can use '*' to allow all header when no credentials,'**' to allow forcefully(it will bring some security risks, be carefully),multiple header use ',' to split. default: *.",
+						"default": "*"
+					},
+					"allow_origins": {
+						"type": "string",
+						"description": "you can use '*' to allow all origins when no credentials,'**' to allow forcefully(it will bring some security risks, be carefully),multiple origin use ',' to split. default: *.",
+						"default": "*"
+					},
+					"allow_methods": {
+						"type": "string",
+						"description": "you can use '*' to allow all methods when no credentials and '**','**' to allow forcefully(it will bring some security risks, be carefully),multiple method use ',' to split. default: *.",
+						"default": "*"
+					},
+					"expose_headers": {
+						"type": "string",
+						"description": "you can use '*' to expose all header when no credentials,multiple header use ',' to split. default: *.",
+						"default": "*"
+					},
+					"max_age": {
+						"type": "integer",
+						"description": "maximum number of seconds the results can be cached.-1 mean no cached,the max value is depend on browser,more detail plz check MDN. default: 5.",
+						"default": 5
+					}
+				},
+				"type": "object"
+			}
+		},
+		"hmac-auth": {
+			"schema": {
+				"additionalProperties": false,
+				"type": "object",
+				"title": "work with route or service object",
+				"properties": {}
+			},
+			"consumer_schema": {
+				"type": "object",
+				"title": "work with consumer object",
+				"properties": {
+					"signed_headers": {
+						"items": {
+							"type": "string",
+							"maxLength": 50,
+							"minLength": 1
+						},
+						"type": "array"
+					},
+					"access_key": {
+						"type": "string",
+						"maxLength": 256,
+						"minLength": 1
+					},
+					"clock_skew": {
+						"default": 0,
+						"type": "integer"
+					},
+					"algorithm": {
+						"type": "string",
+						"default": "hmac-sha256",
+						"enum": ["hmac-sha1", "hmac-sha256", "hmac-sha512"]
+					},
+					"secret_key": {
+						"type": "string",
+						"maxLength": 256,
+						"minLength": 1
+					},
+					"keep_headers": {
+						"type": "boolean",
+						"title": "whether to keep the http request header",
+						"default": false
+					}
+				},
+				"additionalProperties": false,
+				"required": ["access_key", "secret_key"]
+			}
+		},
+		"grpc-transcode": {
+			"schema": {
+				"required": ["proto_id", "service", "method"],
+				"type": "object",
+				"additionalProperties": true,
+				"properties": {
+					"method": {
+						"description": "the method name in the grpc service.",
+						"type": "string"
+					},
+					"proto_id": {
+						"anyOf": [{
+							"maxLength": 64,
+							"type": "string",
+							"pattern": "^[a-zA-Z0-9-_]+$",
+							"minLength": 1
+						}, {
+							"minimum": 1,
+							"type": "integer"
+						}]
+					},
+					"pb_option": {
+						"items": {
+							"anyOf": [{
+								"type": "string",
+								"description": "enum as result",
+								"enum": ["int64_as_number", "int64_as_string", "int64_as_hexstring"]
+							}, {
+								"type": "string",
+								"description": "int64 as result",
+								"enum": ["ienum_as_name", "enum_as_value"]
+							}, {
+								"type": "string",
+								"description": "default values option",
+								"enum": ["auto_default_values", "no_default_values", "use_default_values", "use_default_metatable"]
+							}, {
+								"type": "string",
+								"description": "hooks option",
+								"enum": ["enable_hooks", "disable_hooks"]
+							}],
+							"type": "string"
+						},
+						"type": "array",
+						"minItems": 1
+					},
+					"service": {
+						"description": "the grpc service name",
+						"type": "string"
+					},
+					"deadline": {
+						"type": "number",
+						"description": "deadline for grpc, millisecond",
+						"default": 0
+					}
+				}
+			}
+		},
+		"limit-count": {
+			"schema": {
+				"dependencies": {
+					"policy": {
+						"oneOf": [{
+							"properties": {
+								"policy": {
+									"enum": ["local"]
+								}
+							}
+						}, {
+							"required": ["redis_host"],
+							"properties": {
+								"redis_timeout": {
+									"minimum": 1,
+									"type": "integer",
+									"default": 1000
+								},
+								"redis_host": {
+									"minLength": 2,
+									"type": "string"
+								},
+								"policy": {
+									"enum": ["redis"]
+								},
+								"redis_password": {
+									"minLength": 0,
+									"type": "string"
+								},
+								"redis_port": {
+									"minimum": 1,
+									"type": "integer",
+									"default": 6379
+								}
+							}
+						}, {
+							"required": ["redis_cluster_nodes"],
+							"properties": {
+								"redis_cluster_nodes": {
+									"items": {
+										"type": "string",
+										"maxLength": 100,
+										"minLength": 2
+									},
+									"type": "array",
+									"minItems": 2
+								},
+								"policy": {
+									"enum": ["redis-cluster"]
+								},
+								"redis_password": {
+									"minLength": 0,
+									"type": "string"
+								},
+								"redis_timeout": {
+									"minimum": 1,
+									"type": "integer",
+									"default": 1000
+								}
+							}
+						}]
+					}
+				},
+				"type": "object",
+				"required": ["count", "time_window", "key"],
+				"properties": {
+					"count": {
+						"minimum": 0,
+						"type": "integer"
+					},
+					"rejected_code": {
+						"minimum": 200,
+						"type": "integer",
+						"default": 503,
+						"maximum": 600
+					},
+					"policy": {
+						"type": "string",
+						"default": "local",
+						"enum": ["local", "redis", "redis-cluster"]
+					},
+					"time_window": {
+						"minimum": 0,
+						"type": "integer"
+					},
+					"key": {
+						"enum": ["remote_addr", "server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name", "service_id"],
+						"type": "string"
+					}
+				}
+			}
+		},
+		"key-auth": {
+			"schema": {
+				"type": "object",
+				"additionalProperties": false,
+				"properties": {}
+			},
+			"consumer_schema": {
+				"required": ["key"],
+				"type": "object",
+				"additionalProperties": false,
+				"properties": {
+					"key": {
+						"type": "string"
+					}
+				}
+			}
+		},
+		"authz-keycloak": {
+			"schema": {
+				"type": "object",
+				"required": ["token_endpoint"],
+				"properties": {
+					"keepalive": {
+						"default": true,
+						"type": "boolean"
+					},
+					"grant_type": {
+						"type": "string",
+						"default": "urn:ietf:params:oauth:grant-type:uma-ticket",
+						"enum": ["urn:ietf:params:oauth:grant-type:uma-ticket"],
+						"maxLength": 100,
+						"minLength": 1
+					},
+					"timeout": {
+						"minimum": 1000,
+						"type": "integer",
+						"default": 3000
+					},
+					"policy_enforcement_mode": {
+						"type": "string",
+						"default": "ENFORCING",
+						"enum": ["ENFORCING", "PERMISSIVE"]
+					},
+					"audience": {
+						"type": "string",
+						"maxLength": 100,
+						"minLength": 1
+					},
+					"permissions": {
+						"items": {
+							"type": "string",
+							"maxLength": 100,
+							"minLength": 1
+						},
+						"type": "array",
+						"uniqueItems": true
+					},
+					"keepalive_timeout": {
+						"minimum": 1000,
+						"type": "integer",
+						"default": 60000
+					},
+					"ssl_verify": {
+						"default": true,
+						"type": "boolean"
+					},
+					"keepalive_pool": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 5
+					},
+					"token_endpoint": {
+						"type": "string",
+						"maxLength": 4096,
+						"minLength": 1
+					}
+				}
+			}
+		},
+		"wolf-rbac": {
+			"schema": {
+				"properties": {
+					"appid": {
+						"default": "unset",
+						"type": "string"
+					},
+					"header_prefix": {
+						"default": "X-",
+						"type": "string"
+					},
+					"server": {
+						"default": "http://127.0.0.1:10080",
+						"type": "string"
+					}
+				},
+				"type": "object"
+			}
+		},
+		"kafka-logger": {
+			"schema": {
+				"type": "object",
+				"required": ["broker_list", "kafka_topic", "key"],
+				"properties": {
+					"meta_format": {
+						"type": "string",
+						"default": "default",
+						"enum": ["default", "origin"]
+					},
+					"timeout": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 3
+					},
+					"max_retry_count": {
+						"minimum": 0,
+						"type": "integer",
+						"default": 0
+					},
+					"include_req_body": {
+						"default": false,
+						"type": "boolean"
+					},
+					"batch_max_size": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 1000
+					},
+					"inactive_timeout": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 5
+					},
+					"key": {
+						"type": "string"
+					},
+					"buffer_duration": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 60
+					},
+					"kafka_topic": {
+						"type": "string"
+					},
+					"retry_delay": {
+						"minimum": 0,
+						"type": "integer",
+						"default": 1
+					},
+					"broker_list": {
+						"type": "object"
+					},
+					"name": {
+						"default": "kafka logger",
+						"type": "string"
+					}
+				}
+			}
+		},
+		"openid-connect": {
+			"schema": {
+				"type": "object",
+				"required": ["client_id", "client_secret", "discovery"],
+				"properties": {
+					"logout_path": {
+						"type": "string"
+					},
+					"introspection_endpoint_auth_method": {
+						"type": "string"
+					},
+					"client_secret": {
+						"type": "string"
+					},
+					"scope": {
+						"type": "string"
+					},
+					"introspection_endpoint": {
+						"type": "string"
+					},
+					"discovery": {
+						"type": "string"
+					},
+					"public_key": {
+						"type": "string"
+					},
+					"ssl_verify": {
+						"type": "boolean"
+					},
+					"redirect_uri": {
+						"type": "string"
+					},
+					"realm": {
+						"type": "string"
+					},
+					"bearer_only": {
+						"type": "boolean"
+					},
+					"timeout": {
+						"minimum": 1,
+						"type": "integer"
+					},
+					"token_signing_alg_values_expected": {
+						"type": "string"
+					},
+					"client_id": {
+						"type": "string"
+					}
+				}
+			}
+		},
+		"limit-conn": {
+			"schema": {
+				"type": "object",
+				"required": ["conn", "burst", "default_conn_delay", "key"],
+				"properties": {
+					"conn": {
+						"exclusiveMinimum": 0,
+						"type": "integer"
+					},
+					"burst": {
+						"minimum": 0,
+						"type": "integer"
+					},
+					"rejected_code": {
+						"minimum": 200,
+						"type": "integer",
+						"default": 503
+					},
+					"key": {
+						"enum": ["remote_addr", "server_addr", "http_x_real_ip", "http_x_forwarded_for", "consumer_name"],
+						"type": "string"
+					},
+					"default_conn_delay": {
+						"exclusiveMinimum": 0,
+						"type": "number"
+					}
+				}
+			}
+		},
+		"redirect": {
+			"schema": {
+				"oneOf": [{
+					"required": ["uri"]
+				}, {
+					"required": ["http_to_https"]
+				}],
+				"type": "object",
+				"properties": {
+					"ret_code": {
+						"minimum": 200,
+						"type": "integer",
+						"default": 302
+					},
+					"uri": {
+						"minLength": 2,
+						"type": "string"
+					},
+					"http_to_https": {
+						"type": "boolean"
+					}
+				}
+			}
+		},
+		"echo": {
+			"schema": {
+				"minProperties": 1,
+				"type": "object",
+				"additionalProperties": false,
+				"properties": {
+					"before_body": {
+						"description": "body before the filter phase.",
+						"type": "string"
+					},
+					"auth_value": {
+						"description": "auth value",
+						"type": "string"
+					},
+					"headers": {
+						"minProperties": 1,
+						"type": "object",
+						"description": "new headers for response"
+					},
+					"after_body": {
+						"description": "body after the modification of filter phase.",
+						"type": "string"
+					},
+					"body": {
+						"description": "body to replace upstream response.",
+						"type": "string"
+					}
+				},
+				"anyOf": [{
+					"required": ["before_body"]
+				}, {
+					"required": ["body"]
+				}, {
+					"required": ["after_body"]
+				}]
+			}
+		},
+		"proxy-cache": {
+			"schema": {
+				"type": "object",
+				"required": ["cache_zone"],
+				"properties": {
+					"no_cache": {
+						"items": {
+							"pattern": "(^[^\\$].+$|^\\$[0-9a-zA-Z_]+$)",
+							"type": "string"
+						},
+						"type": "array",
+						"minItems": 1
+					},
+					"cache_bypass": {
+						"items": {
+							"pattern": "(^[^\\$].+$|^\\$[0-9a-zA-Z_]+$)",
+							"type": "string"
+						},
+						"type": "array",
+						"minItems": 1
+					},
+					"cache_method": {
+						"items": {
+							"type": "string",
+							"description": "http method",
+							"enum": ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE"]
+						},
+						"type": "array",
+						"default": ["GET", "HEAD"],
+						"minItems": 1,
+						"uniqueItems": true
+					},
+					"cache_zone": {
+						"minLength": 1,
+						"type": "string"
+					},
+					"hide_cache_headers": {
+						"default": false,
+						"type": "boolean"
+					},
+					"cache_key": {
+						"items": {
+							"type": "string",
+							"description": "a key for caching",
+							"pattern": "(^[^\\$].+$|^\\$[0-9a-zA-Z_]+$)"
+						},
+						"type": "array",
+						"minItems": 1,
+						"default": ["$host", "$request_uri"]
+					},
+					"cache_http_status": {
+						"items": {
+							"minimum": 200,
+							"type": "integer",
+							"description": "http response status",
+							"maximum": 599
+						},
+						"type": "array",
+						"default": [200, 301, 404],
+						"minItems": 1,
+						"uniqueItems": true
+					}
+				}
+			}
+		},
+		"udp-logger": {
+			"schema": {
+				"type": "object",
+				"required": ["host", "port"],
+				"properties": {
+					"host": {
+						"type": "string"
+					},
+					"port": {
+						"minimum": 0,
+						"type": "integer"
+					},
+					"timeout": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 3
+					},
+					"include_req_body": {
+						"default": false,
+						"type": "boolean"
+					},
+					"batch_max_size": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 1000
+					},
+					"buffer_duration": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 60
+					},
+					"inactive_timeout": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 5
+					},
+					"name": {
+						"default": "udp logger",
+						"type": "string"
+					}
+				}
+			}
+		},
+		"request-validation": {
+			"schema": {
+				"anyOf": [{
+					"properties": {
+						"body_schema": {
+							"type": "object"
+						}
+					},
+					"title": "Body schema",
+					"required": ["body_schema"]
+				}, {
+					"properties": {
+						"header_schema": {
+							"type": "object"
+						}
+					},
+					"title": "Header schema",
+					"required": ["header_schema"]
+				}],
+				"type": "object"
+			}
+		},
+		"consumer-restriction": {
+			"schema": {
+				"oneOf": [{
+					"properties": {
+						"type": {
+							"type": "string",
+							"default": "consumer_name",
+							"enum": ["consumer_name", "service_id"]
+						},
+						"rejected_code": {
+							"minimum": 200,
+							"type": "integer",
+							"default": 403
+						},
+						"blacklist": {
+							"items": {
+								"type": "string"
+							},
+							"type": "array",
+							"minItems": 1
+						}
+					},
+					"title": "blacklist",
+					"required": ["blacklist"]
+				}, {
+					"properties": {
+						"type": {
+							"type": "string",
+							"default": "consumer_name",
+							"enum": ["consumer_name", "service_id"]
+						},
+						"whitelist": {
+							"items": {
+								"type": "string"
+							},
+							"type": "array",
+							"minItems": 1
+						},
+						"rejected_code": {
+							"minimum": 200,
+							"type": "integer",
+							"default": 403
+						}
+					},
+					"title": "whitelist",
+					"required": ["whitelist"]
+				}],
+				"type": "object"
+			}
+		},
+		"response-rewrite": {
+			"schema": {
+				"minProperties": 1,
+				"type": "object",
+				"additionalProperties": false,
+				"properties": {
+					"status_code": {
+						"minimum": 200,
+						"type": "integer",
+						"description": "new status code for repsonse",
+						"maximum": 598
+					},
+					"body": {
+						"description": "new body for repsonse",
+						"type": "string"
+					},
+					"body_base64": {
+						"type": "boolean",
+						"description": "whether new body for repsonse need base64 decode before return",
+						"default": false
+					},
+					"headers": {
+						"minProperties": 1,
+						"type": "object",
+						"description": "new headers for repsonse"
+					}
+				}
+			}
+		},
+		"syslog": {
+			"schema": {
+				"type": "object",
+				"required": ["host", "port"],
+				"properties": {
+					"tls": {
+						"default": false,
+						"type": "boolean"
+					},
+					"drop_limit": {
+						"default": 1048576,
+						"type": "integer"
+					},
+					"timeout": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 3
+					},
+					"pool_size": {
+						"minimum": 5,
+						"type": "integer",
+						"default": 5
+					},
+					"sock_type": {
+						"type": "string",
+						"default": "tcp",
+						"enum": ["tcp", "udp"]
+					},
+					"include_req_body": {
+						"default": false,
+						"type": "boolean"
+					},
+					"host": {
+						"type": "string"
+					},
+					"port": {
+						"type": "integer"
+					},
+					"name": {
+						"default": "sys logger",
+						"type": "string"
+					},
+					"buffer_duration": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 60
+					},
+					"batch_max_size": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 1000
+					},
+					"retry_interval": {
+						"minimum": 0,
+						"type": "integer",
+						"default": 1
+					},
+					"max_retry_times": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 1
+					},
+					"flush_limit": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 4096
+					}
+				}
+			}
+		},
+		"skywalking": {
+			"schema": {
+				"type": "object",
+				"additionalProperties": false,
+				"properties": {
+					"sample_ratio": {
+						"minimum": 1e-05,
+						"type": "number",
+						"default": 1,
+						"maximum": 1
+					}
+				}
+			}
+		},
+		"node-status": {
+			"schema": {
+				"additionalProperties": false,
+				"type": "object"
+			}
+		},
+		"http-logger": {
+			"schema": {
+				"type": "object",
+				"required": ["uri"],
+				"properties": {
+					"timeout": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 3
+					},
+					"max_retry_count": {
+						"minimum": 0,
+						"type": "integer",
+						"default": 0
+					},
+					"inactive_timeout": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 5
+					},
+					"include_req_body": {
+						"default": false,
+						"type": "boolean"
+					},
+					"batch_max_size": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 1000
+					},
+					"name": {
+						"default": "http logger",
+						"type": "string"
+					},
+					"concat_method": {
+						"type": "string",
+						"default": "json",
+						"enum": ["json", "new_line"]
+					},
+					"auth_header": {
+						"default": "",
+						"type": "string"
+					},
+					"retry_delay": {
+						"minimum": 0,
+						"type": "integer",
+						"default": 1
+					},
+					"uri": {
+						"type": "string"
+					},
+					"buffer_duration": {
+						"minimum": 1,
+						"type": "integer",
+						"default": 60
+					}
+				}
+			}
+		},
+		"fault-injection": {
+			"schema": {
+				"minProperties": 1,
+				"type": "object",
+				"properties": {
+					"delay": {
+						"minProperties": 1,
+						"type": "object",
+						"properties": {
+							"percentage": {
+								"minimum": 0,
+								"type": "integer",
+								"maximum": 100
+							},
+							"duration": {
+								"minimum": 0,
+								"type": "number"
+							}
+						}
+					},
+					"abort": {
+						"minProperties": 1,
+						"type": "object",
+						"properties": {
+							"percentage": {
+								"minimum": 0,
+								"type": "integer",
+								"maximum": 100
+							},
+							"http_status": {
+								"minimum": 200,
+								"type": "integer"
+							},
+							"body": {
+								"minLength": 0,
+								"type": "string"
+							}
+						}
+					}
+				}
+			}
+		},
+		"referer-restriction": {
+			"schema": {
+				"additionalProperties": false,
+				"type": "object",
+				"required": ["whitelist"],
+				"properties": {
+					"bypass_missing": {
+						"default": false,
+						"type": "boolean"
+					},
+					"whitelist": {
+						"items": {
+							"pattern": "^\\*?[0-9a-zA-Z-.]+$",
+							"type": "string"
+						},
+						"type": "array",
+						"minItems": 1
+					}
+				}
+			}
+		},
+		"uri-blocker": {
+			"schema": {
+				"type": "object",
+				"required": ["block_rules"],
+				"properties": {
+					"rejected_code": {
+						"minimum": 200,
+						"type": "integer",
+						"default": 403
+					},
+					"block_rules": {
+						"items": {
+							"type": "string",
+							"maxLength": 4096,
+							"minLength": 1
+						},
+						"type": "array",
+						"uniqueItems": true
+					}
+				}
+			}
+		},
+		"serverless-pre-function": {
+			"schema": {
+				"type": "object",
+				"required": ["functions"],
+				"properties": {
+					"phase": {
+						"enum": ["rewrite", "access", "header_filter", "body_filter", "log", "balancer"],
+						"type": "string"
+					},
+					"functions": {
+						"items": {
+							"type": "string"
+						},
+						"type": "array",
+						"minItems": 1
+					}
+				}
+			}
+		},
+		"proxy-mirror": {
+			"schema": {
+				"minProperties": 1,
+				"type": "object",
+				"required": ["host"],
+				"properties": {
+					"host": {
+						"pattern": "^http(s)?:\\/\\/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:[0-9]{1,5})?$",
+						"type": "string"
+					}
+				}
+			}
+		},
+		"basic-auth": {
+			"schema": {
+				"additionalProperties": false,
+				"type": "object",
+				"title": "work with route or service object",
+				"properties": {}
+			},
+			"consumer_schema": {
+				"type": "object",
+				"title": "work with consumer object",
+				"properties": {
+					"username": {
+						"type": "string"
+					},
+					"password": {
+						"type": "string"
+					}
+				},
+				"additionalProperties": false,
+				"required": ["username", "password"]
+			}
+		},
+		"prometheus": {
+			"schema": {
+				"additionalProperties": false,
+				"type": "object"
+			}
+		},
+		"example-plugin": {
+			"schema": {
+				"type": "object",
+				"required": ["i"],
+				"properties": {
+					"i": {
+						"minimum": 0,
+						"type": "number"
+					},
+					"port": {
+						"type": "integer"
+					},
+					"s": {
+						"type": "string"
+					},
+					"t": {
+						"minItems": 1,
+						"type": "array"
+					},
+					"ip": {
+						"type": "string"
+					}
+				}
+			}
+		},
+		"log-rotate": {
+			"schema": {
+				"type": "object",
+				"additionalProperties": false,
+				"properties": {}
+			}
+		},
+		"api-breaker": {
+			"schema": {
+				"type": "object",
+				"required": ["break_response_code"],
+				"properties": {
+					"max_breaker_sec": {
+						"minimum": 3,
+						"type": "integer",
+						"default": 300
+					},
+					"healthy": {
+						"type": "object",
+						"default": {
+							"http_statuses": [200],
+							"successes": 3
+						},
+						"properties": {
+							"http_statuses": {
+								"items": {
+									"minimum": 200,
+									"type": "integer",
+									"maximum": 499
+								},
+								"type": "array",
+								"default": [200],
+								"minItems": 1,
+								"uniqueItems": true
+							},
+							"successes": {
+								"minimum": 1,
+								"type": "integer",
+								"default": 3
+							}
+						}
+					},
+					"break_response_code": {
+						"minimum": 200,
+						"type": "integer",
+						"maximum": 599
+					},
+					"unhealthy": {
+						"type": "object",
+						"default": {
+							"http_statuses": [500],
+							"failures": 3
+						},
+						"properties": {
+							"http_statuses": {
+								"items": {
+									"minimum": 500,
+									"type": "integer",
+									"maximum": 599
+								},
+								"type": "array",
+								"default": [500],
+								"minItems": 1,
+								"uniqueItems": true
+							},
+							"failures": {
+								"minimum": 1,
+								"type": "integer",
+								"default": 3
+							}
+						}
+					}
+				}
+			}
+		},
+		"batch-requests": {
+			"schema": {
+				"additionalProperties": false,
+				"type": "object"
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/api/docker-compose.yml b/api/docker-compose.yml
deleted file mode 100644
index 5e91b30..0000000
--- a/api/docker-compose.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-version: '3'
-
-services:
-  manager:
-    image: golang:1.13.8
-    volumes:
-      - .:/go/src/github.com/apisix/manager-api
-    working_dir: /go/src/github.com/apisix/manager-api
-    command: go test -v github.com/apisix/manager-api/service
diff --git a/api/entry.sh b/api/entry.sh
index 8f7afbd..886e47c 100755
--- a/api/entry.sh
+++ b/api/entry.sh
@@ -16,22 +16,6 @@
 # limitations under the License.
 #
 
-pwd=`pwd`
-
-# config
-cp ${pwd}/api/conf/conf_preview.json ${pwd}/conf.json
-
-# export APIX_DAG_LIB_PATH="${pwd}/dag-to-lua-1.1/lib/"
-# export APIX_ETCD_ENDPOINTS="127.0.0.1:2379"
-
-export SYSLOG_HOST=127.0.0.1
-
-if [[ "$unamestr" == 'Darwin' ]]; then
-	sed -i '' -e "s%#syslogAddress#%`echo $SYSLOG_HOST`%g" ${pwd}/conf.json
-else
-	sed -i -e "s%#syslogAddress#%`echo $SYSLOG_HOST`%g" ${pwd}/conf.json
-fi
-
-cp ${pwd}/conf.json ${pwd}/api/conf/conf.json
+export ENV=prod
 
 exec ./manager-api
diff --git a/api/filter/authentication.go b/api/filter/authentication.go
index 9928d2a..ba6c17c 100644
--- a/api/filter/authentication.go
+++ b/api/filter/authentication.go
@@ -33,7 +33,7 @@ func Authentication() gin.HandlerFunc {
 
 			// verify token
 			token, err := jwt.ParseWithClaims(tokenStr, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
-				return []byte(conf.AuthenticationConfig.Session.Secret), nil
+				return []byte(conf.AuthConf.Secret), nil
 			})
 
 			errResp := gin.H{
diff --git a/api/filter/logging.go b/api/filter/logging.go
index badf234..328ac4d 100644
--- a/api/filter/logging.go
+++ b/api/filter/logging.go
@@ -16,72 +16,4 @@
  */
 package filter
 
-import (
-	"bytes"
-	"io/ioutil"
-	"time"
-
-	"github.com/gin-gonic/gin"
-	"github.com/sirupsen/logrus"
-)
-
-func RequestLogHandler() gin.HandlerFunc {
-	return func(c *gin.Context) {
-		start, host, remoteIP, path, method := time.Now(), c.Request.Host, c.ClientIP(), c.Request.URL.Path, c.Request.Method
-		var val interface{}
-		if method == "GET" {
-			val = c.Request.URL.Query()
-		} else {
-			val, _ = c.GetRawData()
-
-			// set RequestBody back
-			c.Request.Body = ioutil.NopCloser(bytes.NewReader(val.([]byte)))
-		}
-		c.Set("requestBody", val)
-		uuid, _ := c.Get("X-Request-Id")
-
-		param, _ := c.Get("requestBody")
-
-		switch param.(type) {
-		case []byte:
-			param = string(param.([]byte))
-		default:
-		}
-
-		blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
-		c.Writer = blw
-		c.Next()
-		latency := time.Now().Sub(start) / 1000000
-		statusCode := c.Writer.Status()
-		respBody := blw.body.String()
-		if uuid == "" {
-			uuid = c.Writer.Header().Get("X-Request-Id")
-		}
-		var errs []string
-		for _, err := range c.Errors {
-			errs = append(errs, err.Error())
-		}
-		logger.WithFields(logrus.Fields{
-			"requestId":  uuid,
-			"latency":    latency,
-			"remoteIp":   remoteIP,
-			"method":     method,
-			"path":       path,
-			"statusCode": statusCode,
-			"host":       host,
-			"params":     param,
-			"respBody":   respBody,
-			"errMsg":     errs,
-		}).Info("")
-	}
-}
-
-type bodyLogWriter struct {
-	gin.ResponseWriter
-	body *bytes.Buffer
-}
-
-func (w bodyLogWriter) Write(b []byte) (int, error) {
-	w.body.Write(b)
-	return w.ResponseWriter.Write(b)
-}
+//for logging access log, will refactor it in a new pr.
diff --git a/api/filter/recover.go b/api/filter/recover.go
index a47e270..77e44a3 100644
--- a/api/filter/recover.go
+++ b/api/filter/recover.go
@@ -26,11 +26,9 @@ import (
 
 	"github.com/apisix/manager-api/log"
 	"github.com/gin-gonic/gin"
-	"github.com/sirupsen/logrus"
 )
 
 var (
-	logger    = log.GetLogger()
 	dunno     = []byte("???")
 	centerDot = []byte("·")
 	dot       = []byte(".")
@@ -41,12 +39,13 @@ func RecoverHandler() gin.HandlerFunc {
 	return func(c *gin.Context) {
 		defer func() {
 			if err := recover(); err != nil {
-				uuid := c.Writer.Header().Get("X-Request-Id")
-				logger.WithFields(logrus.Fields{
-					"uuid": uuid,
-				})
+				fmt.Println("err;", err)
+				//uuid := c.Writer.Header().Get("X-Request-Id")
 				stack := stack(3)
-				logger.Errorf("[Recovery] %s panic recovered:\n\n%s\n%s", timeFormat(time.Now()), err, stack)
+				fmt.Printf("[Recovery] %s panic recovered:\n\n%s\n%s", timeFormat(time.Now()), err, stack)
+
+				//log.With(zap.String("uuid", uuid))
+				log.Errorf("[Recovery] %s panic recovered:\n\n%s\n%s", timeFormat(time.Now()), err, stack)
 				c.AbortWithStatus(http.StatusInternalServerError)
 			}
 		}()
@@ -58,7 +57,7 @@ func WrapGo(f func(...interface{}), args ...interface{}) {
 	defer func() {
 		if err := recover(); err != nil {
 			stack := stack(3)
-			logger.Errorf("[Recovery] %s panic recovered:\n\n%s\n%s", timeFormat(time.Now()), err, stack)
+			log.Errorf("[Recovery] %s panic recovered:\n\n%s\n%s", timeFormat(time.Now()), err, stack)
 		}
 	}()
 	f(args...)
diff --git a/api/go.mod b/api/go.mod
index 3f41ce6..d054357 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -17,6 +17,9 @@ require (
 	github.com/gin-gonic/gin v1.6.3
 	github.com/gogo/protobuf v1.3.1 // indirect
 	github.com/google/uuid v1.1.2 // indirect
+	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
+	github.com/lestrrat-go/strftime v1.0.3 // indirect
+	github.com/natefinch/lumberjack v2.0.0+incompatible
 	github.com/satori/go.uuid v1.2.0
 	github.com/shiningrush/droplet v0.2.1
 	github.com/shiningrush/droplet/wrapper/gin v0.2.0
@@ -27,4 +30,5 @@ require (
 	github.com/xeipuuv/gojsonschema v1.2.0
 	go.etcd.io/etcd v3.3.25+incompatible
 	go.uber.org/zap v1.16.0
+	gopkg.in/yaml.v2 v2.3.0
 )
diff --git a/api/go.sum b/api/go.sum
index 37089e0..c1e2010 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -1,43 +1,14 @@
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/api7/go-jsonpatch v0.0.0-20180223123257-a8710867776e h1:TX/8xM53DHaIIBr4wU+ifYI8IkUzS8+HrX8kzIzaaG0=
 github.com/api7/go-jsonpatch v0.0.0-20180223123257-a8710867776e/go.mod h1:yc49guNPyTy2deyszk3erQN+2vO2NwLKPNicXurj7Hs=
-github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
 github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
 github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
 github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
-github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/etcd v3.3.25+incompatible h1:0GQEw6h3YnuOVdtwygkIfJ+Omx0tZ8/QkVyXI4LkbeY=
 github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
@@ -46,18 +17,11 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
-github.com/dgrijalva/jwt-go v1.0.2 h1:KPldsxuKGsS2FPWsNeg9ZO18aCrGKujPoWXn2yo+KQM=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0=
 github.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0=
 github.com/gin-contrib/sessions v0.0.3 h1:PoBXki+44XdJdlgDqDrY5nDVe3Wk7wDV/UCOuLP6fBI=
@@ -71,10 +35,6 @@ github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwv
 github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
 github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
 github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
@@ -85,19 +45,11 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
 github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
 github.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=
 github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
 github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@@ -109,23 +61,15 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
 github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
@@ -133,131 +77,64 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+
 github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
 github.com/gorilla/sessions v1.1.3 h1:uXoZdcdA5XdXF3QzuSlheVRUvjl+1rKY7zBXL68L9RU=
 github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
 github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
 github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
-github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
+github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
+github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
+github.com/lestrrat-go/strftime v1.0.3 h1:qqOPU7y+TM8Y803I8fG9c/DyKG3xH/xkng6keC1015Q=
+github.com/lestrrat-go/strftime v1.0.3/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
 github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
 github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc=
-github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
-github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
-github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
-github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
+github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/quasoft/memstore v0.0.0-20180925164028-84a050167438/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/shiningrush/droplet v0.0.0-20191118073048-00b06fe19ce4/go.mod h1:E/th13n/wtPi+Cj2f0hAAEFeT3gb5xsS6Ob4WRrdxdM=
 github.com/shiningrush/droplet v0.2.1 h1:p2utttTbCfgiL+x0Zrb2jFeWspB7/o+v3e+R94G6nm4=
 github.com/shiningrush/droplet v0.2.1/go.mod h1:akW2vIeamvMD6zj6wIBfzYn6StGXBxwlW3gA+hcHu5M=
 github.com/shiningrush/droplet/wrapper/gin v0.2.0 h1:LHkU+TbSkpePgXrTg3hJoSZlCMS03GeWMl0t+oLkd44=
 github.com/shiningrush/droplet/wrapper/gin v0.2.0/go.mod h1:ZJu+sCRrVXn5Pg618c1KK3Ob2UiXGuPM1ROx5uMM9YQ=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
 github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
-github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
 github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
-github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc=
-github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
 github.com/tidwall/gjson v1.6.1 h1:LRbvNuNuvAiISWg6gxLEFuCe72UKy5hDqhxW/8183ws=
 github.com/tidwall/gjson v1.6.1/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0=
 github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
 github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
-github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
 github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
@@ -268,137 +145,53 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
 github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/etcd v0.5.0-alpha.5 h1:VOolFSo3XgsmnYDLozjvZ6JL6AAwIDu1Yx1y+4EYLDo=
 go.etcd.io/etcd v3.3.25+incompatible h1:V1RzkZJj9LqsJRy+TUBgpWSbZXITLB819lstuTFoZOY=
 go.etcd.io/etcd v3.3.25+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
 go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
 go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
 go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
-golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM=
 golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200915084602-288bc346aa39 h1:356XA7ITklAU2//sYkjFeco+dH1bCRD8XCJ9FIEsvo4=
-golang.org/x/sys v0.0.0-20200915084602-288bc346aa39/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
@@ -413,28 +206,17 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
 gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
-gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
-gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/api/internal/core/entity/entity.go b/api/internal/core/entity/entity.go
index c15a5d5..93ae456 100644
--- a/api/internal/core/entity/entity.go
+++ b/api/internal/core/entity/entity.go
@@ -75,6 +75,7 @@ type Route struct {
 	UpstreamID      string                 `json:"upstream_id,omitempty"`
 	ServiceProtocol string                 `json:"service_protocol,omitempty"`
 	Labels          map[string]string      `json:"labels,omitempty"`
+	EnableWebsocket bool                   `json:"enable_websocket,omitempty"`
 }
 
 // --- structures for upstream start  ---
@@ -138,21 +139,20 @@ type HealthChecker struct {
 }
 
 type UpstreamDef struct {
-	Nodes           interface{}       `json:"nodes,omitempty"`
-	Retries         int               `json:"retries,omitempty"`
-	Timeout         interface{}       `json:"timeout,omitempty"`
-	K8sInfo         interface{}       `json:"k8s_deployment_info,omitempty"`
-	Type            string            `json:"type,omitempty"`
-	Checks          interface{}       `json:"checks,omitempty"`
-	HashOn          string            `json:"hash_on,omitempty"`
-	Key             string            `json:"key,omitempty"`
-	EnableWebsocket bool              `json:"enable_websocket,omitempty"`
-	PassHost        string            `json:"pass_host,omitempty"`
-	UpstreamHost    string            `json:"upstream_host,omitempty"`
-	Name            string            `json:"name,omitempty"`
-	Desc            string            `json:"desc,omitempty"`
-	ServiceName     string            `json:"service_name,omitempty"`
-	Labels          map[string]string `json:"labels,omitempty"`
+	Nodes        interface{}       `json:"nodes,omitempty"`
+	Retries      int               `json:"retries,omitempty"`
+	Timeout      interface{}       `json:"timeout,omitempty"`
+	K8sInfo      interface{}       `json:"k8s_deployment_info,omitempty"`
+	Type         string            `json:"type,omitempty"`
+	Checks       interface{}       `json:"checks,omitempty"`
+	HashOn       string            `json:"hash_on,omitempty"`
+	Key          string            `json:"key,omitempty"`
+	PassHost     string            `json:"pass_host,omitempty"`
+	UpstreamHost string            `json:"upstream_host,omitempty"`
+	Name         string            `json:"name,omitempty"`
+	Desc         string            `json:"desc,omitempty"`
+	ServiceName  string            `json:"service_name,omitempty"`
+	Labels       map[string]string `json:"labels,omitempty"`
 }
 
 type Upstream struct {
@@ -200,13 +200,14 @@ type SSL struct {
 
 type Service struct {
 	BaseInfo
-	Name       string                 `json:"name,omitempty"`
-	Desc       string                 `json:"desc,omitempty"`
-	Upstream   *UpstreamDef           `json:"upstream,omitempty"`
-	UpstreamID string                 `json:"upstream_id,omitempty"`
-	Plugins    map[string]interface{} `json:"plugins,omitempty"`
-	Script     string                 `json:"script,omitempty"`
-	Labels     map[string]string      `json:"labels,omitempty"`
+	Name            string                 `json:"name,omitempty"`
+	Desc            string                 `json:"desc,omitempty"`
+	Upstream        *UpstreamDef           `json:"upstream,omitempty"`
+	UpstreamID      string                 `json:"upstream_id,omitempty"`
+	Plugins         map[string]interface{} `json:"plugins,omitempty"`
+	Script          string                 `json:"script,omitempty"`
+	Labels          map[string]string      `json:"labels,omitempty"`
+	EnableWebsocket bool                   `json:"enable_websocket,omitempty"`
 }
 
 type Script struct {
diff --git a/api/internal/core/entity/format.go b/api/internal/core/entity/format.go
index 0c5d8d1..a409644 100644
--- a/api/internal/core/entity/format.go
+++ b/api/internal/core/entity/format.go
@@ -23,8 +23,8 @@ import (
 )
 
 func NodesFormat(obj interface{}) interface{} {
+	var nodes []*Node
 	if value, ok := obj.(map[string]float64); ok {
-		var nodes []*Node
 		var strArr []string
 		for key, val := range value {
 			node := &Node{}
@@ -48,5 +48,22 @@ func NodesFormat(obj interface{}) interface{} {
 		return nodes
 	}
 
+	if nodes, ok := obj.([]*Node); ok {
+		return nodes
+	}
+
+	if list, ok := obj.([]interface{}); ok {
+		for _, v := range list {
+			val := v.(map[string]interface{})
+			node := &Node{}
+			node.Host = val["host"].(string)
+			node.Port = int(val["port"].(float64))
+			node.Weight = int(val["weight"].(float64))
+			nodes = append(nodes, node)
+		}
+
+		return nodes
+	}
+
 	return obj
 }
diff --git a/api/internal/core/entity/format_test.go b/api/internal/core/entity/format_test.go
index cc51bf2..5ba5e13 100644
--- a/api/internal/core/entity/format_test.go
+++ b/api/internal/core/entity/format_test.go
@@ -28,7 +28,9 @@ func TestConsumer(t *testing.T) {
     "127.0.0.1:8080": 1
   }`
 	nodesMap := map[string]float64{}
-	json.Unmarshal([]byte(nodesStr), &nodesMap)
+	err := json.Unmarshal([]byte(nodesStr), &nodesMap)
+	assert.Nil(t, err)
+
 	res := NodesFormat(nodesMap)
 	nodes := res.([]*Node)
 
diff --git a/api/internal/core/storage/etcd.go b/api/internal/core/storage/etcd.go
index aff2d4a..5a9c23e 100644
--- a/api/internal/core/storage/etcd.go
+++ b/api/internal/core/storage/etcd.go
@@ -19,9 +19,10 @@ package storage
 import (
 	"context"
 	"fmt"
+	"time"
+
 	"github.com/apisix/manager-api/internal/utils"
 	"go.etcd.io/etcd/clientv3"
-	"time"
 )
 
 var (
@@ -132,6 +133,8 @@ func (s *EtcdV3Storage) Watch(ctx context.Context, key string) <-chan WatchRespo
 			}
 			ch <- output
 		}
+
+		close(ch)
 	}()
 
 	return ch
diff --git a/api/internal/core/store/query.go b/api/internal/core/store/query.go
index 7f59594..20036d0 100644
--- a/api/internal/core/store/query.go
+++ b/api/internal/core/store/query.go
@@ -16,6 +16,7 @@ package store
 
 import (
 	"github.com/apisix/manager-api/internal/core/entity"
+	"github.com/apisix/manager-api/log"
 )
 
 type Query struct {
@@ -88,7 +89,7 @@ func NewQuery(sort *Sort, filter *Filter, pagination *Pagination) *Query {
 
 func NewSort(sortRaw []string) *Sort {
 	if sortRaw == nil || len(sortRaw)%2 == 1 {
-		// Empty sort list or invalid (odd) length
+		log.Info("empty sort for query")
 		return NoSort
 	}
 	list := []SortBy{}
@@ -117,6 +118,7 @@ func NewSort(sortRaw []string) *Sort {
 
 func NewFilter(filterRaw []string) *Filter {
 	if filterRaw == nil || len(filterRaw)%2 == 1 {
+		log.Info("empty filter for query")
 		return NoFilter
 	}
 	list := []FilterBy{}
diff --git a/api/internal/core/store/store.go b/api/internal/core/store/store.go
index b285183..1627e2e 100644
--- a/api/internal/core/store/store.go
+++ b/api/internal/core/store/store.go
@@ -21,7 +21,6 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"log"
 	"reflect"
 	"sort"
 	"sync"
@@ -31,6 +30,7 @@ import (
 
 	"github.com/apisix/manager-api/internal/core/entity"
 	"github.com/apisix/manager-api/internal/core/storage"
+	"github.com/apisix/manager-api/log"
 )
 
 type Interface interface {
@@ -60,12 +60,15 @@ type GenericStoreOption struct {
 
 func NewGenericStore(opt GenericStoreOption) (*GenericStore, error) {
 	if opt.BasePath == "" {
+		log.Warn("base path empty")
 		return nil, fmt.Errorf("base path can not be empty")
 	}
 	if opt.ObjType == nil {
+		log.Warn("object type is nil")
 		return nil, fmt.Errorf("object type can not be nil")
 	}
 	if opt.KeyFunc == nil {
+		log.Warn("key func is nil")
 		return nil, fmt.Errorf("key func can not be nil")
 	}
 
@@ -73,6 +76,7 @@ func NewGenericStore(opt GenericStoreOption) (*GenericStore, error) {
 		opt.ObjType = opt.ObjType.Elem()
 	}
 	if opt.ObjType.Kind() != reflect.Struct {
+		log.Warn("obj type is invalid")
 		return nil, fmt.Errorf("obj type is invalid")
 	}
 	s := &GenericStore{
@@ -106,7 +110,7 @@ func (s *GenericStore) Init() error {
 	go func() {
 		for event := range ch {
 			if event.Canceled {
-				log.Println("watch failed", event.Error)
+				log.Warnf("watch failed: %w", event.Error)
 			}
 
 			for i := range event.Events {
@@ -114,7 +118,7 @@ func (s *GenericStore) Init() error {
 				case storage.EventTypePut:
 					objPtr, err := s.StringToObjPtr(event.Events[i].Value)
 					if err != nil {
-						log.Println("value convert to obj failed", err)
+						log.Warnf("value convert to obj failed: %w", err)
 						continue
 					}
 					s.cache.Store(event.Events[i].Key[len(s.opt.BasePath)+1:], objPtr)
@@ -131,6 +135,7 @@ func (s *GenericStore) Init() error {
 func (s *GenericStore) Get(key string) (interface{}, error) {
 	ret, ok := s.cache.Load(key)
 	if !ok {
+		log.Warnf("data not found by key: %s", key)
 		return nil, data.ErrNotFound
 	}
 	return ret, nil
@@ -213,6 +218,7 @@ func (s *GenericStore) List(input ListInput) (*ListOutput, error) {
 func (s *GenericStore) ingestValidate(obj interface{}) (err error) {
 	if s.opt.Validator != nil {
 		if err := s.opt.Validator.Validate(obj); err != nil {
+			log.Infof("data validate fail: %w", err)
 			return err
 		}
 	}
@@ -244,11 +250,13 @@ func (s *GenericStore) Create(ctx context.Context, obj interface{}) error {
 	}
 	_, ok := s.cache.Load(key)
 	if ok {
+		log.Warnf("key: %s is conflicted", key)
 		return fmt.Errorf("key: %s is conflicted", key)
 	}
 
 	bs, err := json.Marshal(obj)
 	if err != nil {
+		log.Warnf("json marshal failed: %s", err)
 		return fmt.Errorf("json marshal failed: %s", err)
 	}
 	if err := s.Stg.Create(ctx, s.GetObjStorageKey(obj), string(bs)); err != nil {
@@ -272,6 +280,7 @@ func (s *GenericStore) Update(ctx context.Context, obj interface{}, createIfNotE
 		if createIfNotExist {
 			return s.Create(ctx, obj)
 		}
+		log.Warnf("key: %s is not found", key)
 		return fmt.Errorf("key: %s is not found", key)
 	}
 
@@ -284,6 +293,7 @@ func (s *GenericStore) Update(ctx context.Context, obj interface{}, createIfNotE
 
 	bs, err := json.Marshal(obj)
 	if err != nil {
+		log.Warnf("json marshal failed: %s", err)
 		return fmt.Errorf("json marshal failed: %s", err)
 	}
 	if err := s.Stg.Update(ctx, s.GetObjStorageKey(obj), string(bs)); err != nil {
@@ -311,6 +321,7 @@ func (s *GenericStore) StringToObjPtr(str string) (interface{}, error) {
 	objPtr := reflect.New(s.opt.ObjType)
 	err := json.Unmarshal([]byte(str), objPtr.Interface())
 	if err != nil {
+		log.Warnf("json marshal failed: %s", err)
 		return nil, fmt.Errorf("json unmarshal failed: %w", err)
 	}
 
diff --git a/api/internal/core/store/store_test.go b/api/internal/core/store/store_test.go
index 53f6a85..17a1e83 100644
--- a/api/internal/core/store/store_test.go
+++ b/api/internal/core/store/store_test.go
@@ -580,7 +580,8 @@ func TestGenericStore_Create(t *testing.T) {
 			createCalled = true
 			assert.Equal(t, tc.wantKey, args[1], tc.caseDesc)
 			input := TestStruct{}
-			_ = json.Unmarshal([]byte(args[2].(string)), &input)
+			err := json.Unmarshal([]byte(args[2].(string)), &input)
+			assert.Nil(t, err)
 			assert.Equal(t, tc.giveObj.Field1, input.Field1, tc.caseDesc)
 			assert.Equal(t, tc.giveObj.Field2, input.Field2, tc.caseDesc)
 			assert.NotEqual(t, 0, len(input.ID), tc.caseDesc)
@@ -689,7 +690,8 @@ func TestGenericStore_Update(t *testing.T) {
 			createCalled = true
 			assert.Equal(t, tc.wantKey, args[1], tc.caseDesc)
 			input := TestStruct{}
-			_ = json.Unmarshal([]byte(args[2].(string)), &input)
+			err := json.Unmarshal([]byte(args[2].(string)), &input)
+			assert.Nil(t, err)
 			assert.Equal(t, tc.giveObj.Field1, input.Field1, tc.caseDesc)
 			assert.Equal(t, tc.giveObj.Field2, input.Field2, tc.caseDesc)
 			assert.NotEqual(t, 0, input.UpdateTime, tc.caseDesc)
diff --git a/api/internal/core/store/storehub.go b/api/internal/core/store/storehub.go
index b2685c9..c38286e 100644
--- a/api/internal/core/store/storehub.go
+++ b/api/internal/core/store/storehub.go
@@ -22,6 +22,7 @@ import (
 
 	"github.com/apisix/manager-api/internal/core/entity"
 	"github.com/apisix/manager-api/internal/utils"
+	"github.com/apisix/manager-api/log"
 )
 
 type HubKey string
@@ -50,9 +51,11 @@ func InitStore(key HubKey, opt GenericStoreOption) error {
 	}
 	s, err := NewGenericStore(opt)
 	if err != nil {
+		log.Warnf("NewGenericStore error: %w", err)
 		return err
 	}
 	if err := s.Init(); err != nil {
+		log.Warnf("GenericStore init error: %w", err)
 		return err
 	}
 
diff --git a/api/internal/core/store/validate.go b/api/internal/core/store/validate.go
index ad3b978..23f7154 100644
--- a/api/internal/core/store/validate.go
+++ b/api/internal/core/store/validate.go
@@ -20,12 +20,14 @@ import (
 	"errors"
 	"fmt"
 	"io/ioutil"
+	"regexp"
 
 	"github.com/xeipuuv/gojsonschema"
 	"go.uber.org/zap/buffer"
 
 	"github.com/apisix/manager-api/conf"
 	"github.com/apisix/manager-api/internal/core/entity"
+	"github.com/apisix/manager-api/log"
 )
 
 type Validator interface {
@@ -75,11 +77,13 @@ type APISIXJsonSchemaValidator struct {
 func NewAPISIXJsonSchemaValidator(jsonPath string) (Validator, error) {
 	schemaDef := conf.Schema.Get(jsonPath).String()
 	if schemaDef == "" {
+		log.Warnf("scheme validate failed: schema not found, path: %s", jsonPath)
 		return nil, fmt.Errorf("scheme validate failed: schema not found, path: %s", jsonPath)
 	}
 
 	s, err := gojsonschema.NewSchema(gojsonschema.NewStringLoader(schemaDef))
 	if err != nil {
+		log.Warnf("new schema failed: %w", err)
 		return nil, fmt.Errorf("new schema failed: %w", err)
 	}
 	return &APISIXJsonSchemaValidator{
@@ -87,19 +91,22 @@ func NewAPISIXJsonSchemaValidator(jsonPath string) (Validator, error) {
 	}, nil
 }
 
-func getPlugins(reqBody interface{}) map[string]interface{} {
-	switch reqBody.(type) {
+func getPlugins(reqBody interface{}) (map[string]interface{}, string) {
+	switch bodyType := reqBody.(type) {
 	case *entity.Route:
+		log.Infof("type of reqBody: %#v", bodyType)
 		route := reqBody.(*entity.Route)
-		return route.Plugins
+		return route.Plugins, "schema"
 	case *entity.Service:
+		log.Infof("type of reqBody: %#v", bodyType)
 		service := reqBody.(*entity.Service)
-		return service.Plugins
+		return service.Plugins, "schema"
 	case *entity.Consumer:
+		log.Infof("type of reqBody: %#v", bodyType)
 		consumer := reqBody.(*entity.Consumer)
-		return consumer.Plugins
+		return consumer.Plugins, "consumer_schema"
 	}
-	return nil
+	return nil, ""
 }
 
 func cHashKeySchemaCheck(upstream *entity.UpstreamDef) error {
@@ -109,7 +116,7 @@ func cHashKeySchemaCheck(upstream *entity.UpstreamDef) error {
 	if upstream.HashOn != "vars" &&
 		upstream.HashOn != "header" &&
 		upstream.HashOn != "cookie" {
-		fmt.Errorf("invalid hash_on type: %s", upstream.HashOn)
+		return fmt.Errorf("invalid hash_on type: %s", upstream.HashOn)
 	}
 
 	var schemaDef string
@@ -187,9 +194,10 @@ func checkUpstream(upstream *entity.UpstreamDef) error {
 }
 
 func checkConf(reqBody interface{}) error {
-	switch reqBody.(type) {
+	switch bodyType := reqBody.(type) {
 	case *entity.Route:
 		route := reqBody.(*entity.Route)
+		log.Infof("type of reqBody: %#v", bodyType)
 		if err := checkUpstream(route.Upstream); err != nil {
 			return err
 		}
@@ -210,6 +218,7 @@ func checkConf(reqBody interface{}) error {
 func (v *APISIXJsonSchemaValidator) Validate(obj interface{}) error {
 	ret, err := v.schema.Validate(gojsonschema.NewGoLoader(obj))
 	if err != nil {
+		log.Warnf("scheme validate failed: %w", err)
 		return fmt.Errorf("scheme validate failed: %w", err)
 	}
 
@@ -229,35 +238,41 @@ func (v *APISIXJsonSchemaValidator) Validate(obj interface{}) error {
 		return err
 	}
 
-	//check plugin json schema
-	plugins := getPlugins(obj)
-	if plugins != nil {
-		for pluginName, pluginConf := range plugins {
-			schemaDef := conf.Schema.Get("plugins." + pluginName).String()
-			if schemaDef == "" {
-				return fmt.Errorf("scheme validate failed: schema not found, path: %s", "plugins."+pluginName)
-			}
+	plugins, schemaType := getPlugins(obj)
+	//fix lua json.encode transform lua{properties={}} to json{"properties":[]}
+	reg := regexp.MustCompile(`\"properties\":\[\]`)
+	for pluginName, pluginConf := range plugins {
+		var schemaDef string
+		schemaDef = conf.Schema.Get("plugins." + pluginName + "." + schemaType).String()
+		if schemaDef == "" && schemaType == "consumer_schema" {
+			schemaDef = conf.Schema.Get("plugins." + pluginName + ".schema").String()
+		}
+		if schemaDef == "" {
+			log.Warnf("scheme validate failed: schema not found, path: %s", "plugins."+pluginName)
+			return fmt.Errorf("scheme validate failed: schema not found, path: %s", "plugins."+pluginName)
+		}
 
-			s, err := gojsonschema.NewSchema(gojsonschema.NewStringLoader(schemaDef))
-			if err != nil {
-				return fmt.Errorf("scheme validate failed: %w", err)
-			}
+		schemaDef = reg.ReplaceAllString(schemaDef, `"properties":{}`)
+		s, err := gojsonschema.NewSchema(gojsonschema.NewStringLoader(schemaDef))
+		if err != nil {
+			log.Warnf("init scheme validate failed: %w", err)
+			return fmt.Errorf("scheme validate failed: %w", err)
+		}
 
-			ret, err := s.Validate(gojsonschema.NewGoLoader(pluginConf))
-			if err != nil {
-				return fmt.Errorf("scheme validate failed: %w", err)
-			}
+		ret, err := s.Validate(gojsonschema.NewGoLoader(pluginConf))
+		if err != nil {
+			return fmt.Errorf("scheme validate failed: %w", err)
+		}
 
-			if !ret.Valid() {
-				errString := buffer.Buffer{}
-				for i, vErr := range ret.Errors() {
-					if i != 0 {
-						errString.AppendString("\n")
-					}
-					errString.AppendString(vErr.String())
+		if !ret.Valid() {
+			errString := buffer.Buffer{}
+			for i, vErr := range ret.Errors() {
+				if i != 0 {
+					errString.AppendString("\n")
 				}
-				return fmt.Errorf("scheme validate failed: %s", errString.String())
+				errString.AppendString(vErr.String())
 			}
+			return fmt.Errorf("scheme validate failed: %s", errString.String())
 		}
 	}
 
diff --git a/api/internal/core/store/validate_test.go b/api/internal/core/store/validate_test.go
index f2494dc..3a47f12 100644
--- a/api/internal/core/store/validate_test.go
+++ b/api/internal/core/store/validate_test.go
@@ -76,20 +76,20 @@ func TestAPISIXJsonSchemaValidator_Validate(t *testing.T) {
 
 	consumer := &entity.Consumer{}
 	reqBody := `{
-      "id": "jack",
-      "username": "jack",
-      "plugins": {
-          "limit-count": {
-              "count": 2,
-              "time_window": 60,
-              "rejected_code": 503,
-              "key": "remote_addr"
-          }
-      },
-    "desc": "test description"
-  }`
-	json.Unmarshal([]byte(reqBody), consumer)
-
+		"id": "jack",
+		"username": "jack",
+		"plugins": {
+		  "limit-count": {
+		      "count": 2,
+		      "time_window": 60,
+		      "rejected_code": 503,
+		      "key": "remote_addr"
+		  }
+		},
+		"desc": "test description"
+	}`
+	err = json.Unmarshal([]byte(reqBody), consumer)
+	assert.Nil(t, err)
 	err = validator.Validate(consumer)
 	assert.Nil(t, err)
 
@@ -106,7 +106,8 @@ func TestAPISIXJsonSchemaValidator_Validate(t *testing.T) {
       },
     "desc": "test description"
   }`
-	json.Unmarshal([]byte(reqBody), consumer2)
+	err = json.Unmarshal([]byte(reqBody), consumer2)
+	assert.Nil(t, err)
 
 	err = validator.Validate(consumer2)
 	assert.NotNil(t, err)
@@ -131,8 +132,8 @@ func TestAPISIXJsonSchemaValidator_Validate(t *testing.T) {
       },
     "desc": "test description"
   }`
-	json.Unmarshal([]byte(reqBody), consumer3)
-
+	err = json.Unmarshal([]byte(reqBody), consumer3)
+	assert.Nil(t, err)
 	err = validator.Validate(consumer3)
 	assert.NotNil(t, err)
 	assert.EqualError(t, err, "scheme validate failed: (root): count is required")
@@ -146,39 +147,39 @@ func TestAPISIXJsonSchemaValidator_checkUpstream(t *testing.T) {
 	// type:chash, hash_on: consumer, missing key, ok
 	route := &entity.Route{}
 	reqBody := `{
-      "id": "1",
-      "methods": ["GET"],
-      "upstream": {
-          "nodes": {
-              "127.0.0.1:8080": 1
-          },
-          "type": "chash",
-          "hash_on":"consumer"
-      },
-      "desc": "new route",
-      "uri": "/index.html"
-  }`
-	json.Unmarshal([]byte(reqBody), route)
-
+		"id": "1",
+		"methods": ["GET"],
+		"upstream": {
+		  "nodes": {
+		      "127.0.0.1:8080": 1
+		  },
+		  "type": "chash",
+		  "hash_on":"consumer"
+		},
+		"desc": "new route",
+		"uri": "/index.html"
+	}`
+	err = json.Unmarshal([]byte(reqBody), route)
+	assert.Nil(t, err)
 	err = validator.Validate(route)
 	assert.Nil(t, err)
 
 	// type:chash, hash_on: default(vars), missing key
 	route2 := &entity.Route{}
 	reqBody = `{
-      "id": "1",
-      "methods": ["GET"],
-      "upstream": {
-          "nodes": {
-              "127.0.0.1:8080": 1
-          },
-          "type": "chash"
-      },
-      "desc": "new route",
-      "uri": "/index.html"
-  }`
-	json.Unmarshal([]byte(reqBody), route2)
-
+		"id": "1",
+		"methods": ["GET"],
+		"upstream": {
+		  "nodes": {
+		      "127.0.0.1:8080": 1
+		  },
+		  "type": "chash"
+		},
+		"desc": "new route",
+		"uri": "/index.html"
+	}`
+	err = json.Unmarshal([]byte(reqBody), route2)
+	assert.Nil(t, err)
 	err = validator.Validate(route2)
 	assert.NotNil(t, err)
 	assert.EqualError(t, err, "missing key")
@@ -186,20 +187,20 @@ func TestAPISIXJsonSchemaValidator_checkUpstream(t *testing.T) {
 	//type:chash, hash_on: header, missing key
 	route3 := &entity.Route{}
 	reqBody = `{
-      "id": "1",
-      "methods": ["GET"],
-      "upstream": {
-          "nodes": {
-              "127.0.0.1:8080": 1
-          },
-          "type": "chash",
-          "hash_on":"header"
-      },
-      "desc": "new route",
-      "uri": "/index.html"
-  }`
-	json.Unmarshal([]byte(reqBody), route3)
-
+		"id": "1",
+		"methods": ["GET"],
+		"upstream": {
+		  "nodes": {
+		      "127.0.0.1:8080": 1
+		  },
+		  "type": "chash",
+		  "hash_on":"header"
+		},
+		"desc": "new route",
+		"uri": "/index.html"
+	}`
+	err = json.Unmarshal([]byte(reqBody), route3)
+	assert.Nil(t, err)
 	err = validator.Validate(route3)
 	assert.NotNil(t, err)
 	assert.EqualError(t, err, "missing key")
@@ -207,20 +208,20 @@ func TestAPISIXJsonSchemaValidator_checkUpstream(t *testing.T) {
 	//type:chash, hash_on: cookie, missing key
 	route4 := &entity.Route{}
 	reqBody = `{
-      "id": "1",
-      "methods": ["GET"],
-      "upstream": {
-          "nodes": {
-              "127.0.0.1:8080": 1
-          },
-          "type": "chash",
-          "hash_on":"cookie"
-      },
-      "desc": "new route",
-      "uri": "/index.html"
-  }`
-	json.Unmarshal([]byte(reqBody), route4)
-
+		"id": "1",
+		"methods": ["GET"],
+		"upstream": {
+		  "nodes": {
+		      "127.0.0.1:8080": 1
+		  },
+		  "type": "chash",
+		  "hash_on":"cookie"
+		},
+		"desc": "new route",
+		"uri": "/index.html"
+	}`
+	err = json.Unmarshal([]byte(reqBody), route4)
+	assert.Nil(t, err)
 	err = validator.Validate(route4)
 	assert.NotNil(t, err)
 	assert.EqualError(t, err, "missing key")
@@ -228,21 +229,21 @@ func TestAPISIXJsonSchemaValidator_checkUpstream(t *testing.T) {
 	//type:chash, hash_on: vars, wrong key
 	route5 := &entity.Route{}
 	reqBody = `{
-      "id": "1",
-      "methods": ["GET"],
-      "upstream": {
-          "nodes": {
-              "127.0.0.1:8080": 1
-          },
-          "type": "chash",
-          "hash_on":"vars",
-          "key": "not_support"
-      },
-      "desc": "new route",
-      "uri": "/index.html"
-  }`
-	json.Unmarshal([]byte(reqBody), route5)
-
+		"id": "1",
+		"methods": ["GET"],
+		"upstream": {
+		  "nodes": {
+		      "127.0.0.1:8080": 1
+		  },
+		  "type": "chash",
+		  "hash_on":"vars",
+		  "key": "not_support"
+		},
+		"desc": "new route",
+		"uri": "/index.html"
+	}`
+	err = json.Unmarshal([]byte(reqBody), route5)
+	assert.Nil(t, err)
 	err = validator.Validate(route5)
 	assert.NotNil(t, err)
 	assert.EqualError(t, err, "scheme validate failed: (root): Does not match pattern '^((uri|server_name|server_addr|request_uri|remote_port|remote_addr|query_string|host|hostname)|arg_[0-9a-zA-z_-]+)$'")
diff --git a/api/internal/handler/authentication/authentication.go b/api/internal/handler/authentication/authentication.go
index 5f3a83b..c3f242d 100644
--- a/api/internal/handler/authentication/authentication.go
+++ b/api/internal/handler/authentication/authentication.go
@@ -66,10 +66,10 @@ func (h *Handler) userLogin(c droplet.Context) (interface{}, error) {
 	claims := jwt.StandardClaims{
 		Subject:   username,
 		IssuedAt:  time.Now().Unix(),
-		ExpiresAt: time.Now().Add(time.Second * time.Duration(conf.AuthenticationConfig.Session.ExpireTime)).Unix(),
+		ExpiresAt: time.Now().Add(time.Second * time.Duration(conf.AuthConf.ExpireTime)).Unix(),
 	}
 	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
-	signedToken, _ := token.SignedString([]byte(conf.AuthenticationConfig.Session.Secret))
+	signedToken, _ := token.SignedString([]byte(conf.AuthConf.Secret))
 
 	// output token
 	return &UserSession{
diff --git a/api/internal/handler/authentication/authentication_test.go b/api/internal/handler/authentication/authentication_test.go
index 01241a9..e1a7e50 100644
--- a/api/internal/handler/authentication/authentication_test.go
+++ b/api/internal/handler/authentication/authentication_test.go
@@ -36,10 +36,11 @@ func TestAuthentication(t *testing.T) {
 	reqBody := `{
 	  "username": "admin",
 	  "password": "admin"
-  }`
-	json.Unmarshal([]byte(reqBody), input)
+	}`
+	err := json.Unmarshal([]byte(reqBody), input)
+	assert.Nil(t, err)
 	ctx.SetInput(input)
-	_, err := handler.userLogin(ctx)
+	_, err = handler.userLogin(ctx)
 	assert.Nil(t, err)
 
 	//username error
@@ -47,8 +48,9 @@ func TestAuthentication(t *testing.T) {
 	reqBody = `{
 	  "username": "sdfasdf",
 	  "password": "admin"
-  }`
-	json.Unmarshal([]byte(reqBody), input2)
+	}`
+	err = json.Unmarshal([]byte(reqBody), input2)
+	assert.Nil(t, err)
 	ctx.SetInput(input2)
 	_, err = handler.userLogin(ctx)
 	assert.EqualError(t, err, "username or password error")
@@ -58,8 +60,9 @@ func TestAuthentication(t *testing.T) {
 	reqBody = `{
 	  "username": "admin",
 	  "password": "admin9384938"
-  }`
-	json.Unmarshal([]byte(reqBody), input3)
+	}`
+	err = json.Unmarshal([]byte(reqBody), input3)
+	assert.Nil(t, err)
 	ctx.SetInput(input3)
 	_, err = handler.userLogin(ctx)
 	assert.EqualError(t, err, "username or password error")
diff --git a/api/internal/handler/consumer/consumer.go b/api/internal/handler/consumer/consumer.go
index 9ceb853..a37c958 100644
--- a/api/internal/handler/consumer/consumer.go
+++ b/api/internal/handler/consumer/consumer.go
@@ -105,6 +105,13 @@ func (h *Handler) Create(c droplet.Context) (interface{}, error) {
 	}
 	input.ID = input.Username
 
+	if _, ok := input.Plugins["jwt-auth"]; ok {
+		jwt := input.Plugins["jwt-auth"].(map[string]interface{})
+		jwt["exp"] = 86400
+
+		input.Plugins["jwt-auth"] = jwt
+	}
+
 	if err := h.consumerStore.Create(c.Context(), input); err != nil {
 		return handler.SpecCodeResponse(err), err
 	}
@@ -128,6 +135,13 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
 	}
 	input.Consumer.ID = input.Consumer.Username
 
+	if _, ok := input.Consumer.Plugins["jwt-auth"]; ok {
+		jwt := input.Consumer.Plugins["jwt-auth"].(map[string]interface{})
+		jwt["exp"] = 86400
+
+		input.Consumer.Plugins["jwt-auth"] = jwt
+	}
+
 	if err := h.consumerStore.Update(c.Context(), &input.Consumer, true); err != nil {
 		//if not exists, create
 		if err.Error() == fmt.Sprintf("key: %s is not found", input.Username) {
diff --git a/api/internal/handler/consumer/consumer_test.go b/api/internal/handler/consumer/consumer_test.go
index 22664fa..1f88c52 100644
--- a/api/internal/handler/consumer/consumer_test.go
+++ b/api/internal/handler/consumer/consumer_test.go
@@ -57,7 +57,8 @@ func TestConsumer(t *testing.T) {
       },
     "desc": "test description"
   }`
-	json.Unmarshal([]byte(reqBody), consumer)
+	err = json.Unmarshal([]byte(reqBody), consumer)
+	assert.Nil(t, err)
 	ctx.SetInput(consumer)
 	_, err = handler.Create(ctx)
 	assert.Nil(t, err)
@@ -65,18 +66,19 @@ func TestConsumer(t *testing.T) {
 	//create consumer 2
 	consumer2 := &entity.Consumer{}
 	reqBody = `{
-      "username": "pony",
-      "plugins": {
-          "limit-count": {
-              "count": 2,
-              "time_window": 60,
-              "rejected_code": 503,
-              "key": "remote_addr"
-          }
-      },
-    "desc": "test description"
-  }`
-	json.Unmarshal([]byte(reqBody), consumer2)
+		"username": "pony",
+		"plugins": {
+		  "limit-count": {
+		      "count": 2,
+		      "time_window": 60,
+		      "rejected_code": 503,
+		      "key": "remote_addr"
+		  }
+		},
+		"desc": "test description"
+	}`
+	err = json.Unmarshal([]byte(reqBody), consumer2)
+	assert.Nil(t, err)
 	ctx.SetInput(consumer2)
 	_, err = handler.Create(ctx)
 	assert.Nil(t, err)
@@ -87,7 +89,8 @@ func TestConsumer(t *testing.T) {
 	//get consumer
 	input := &GetInput{}
 	reqBody = `{"username": "jack"}`
-	json.Unmarshal([]byte(reqBody), input)
+	err = json.Unmarshal([]byte(reqBody), input)
+	assert.Nil(t, err)
 	ctx.SetInput(input)
 	ret, err := handler.Get(ctx)
 	stored := ret.(*entity.Consumer)
@@ -99,18 +102,19 @@ func TestConsumer(t *testing.T) {
 	consumer3 := &UpdateInput{}
 	consumer3.Username = "pony"
 	reqBody = `{
-      "username": "pony",
-      "plugins": {
-          "limit-count": {
-              "count": 2,
-              "time_window": 60,
-              "rejected_code": 503,
-              "key": "remote_addr"
-          }
-      },
-    "desc": "test description2"
-  }`
-	json.Unmarshal([]byte(reqBody), consumer3)
+		"username": "pony",
+		"plugins": {
+		  "limit-count": {
+		      "count": 2,
+		      "time_window": 60,
+		      "rejected_code": 503,
+		      "key": "remote_addr"
+		  }
+		},
+		"desc": "test description2"
+	}`
+	err = json.Unmarshal([]byte(reqBody), consumer3)
+	assert.Nil(t, err)
 	ctx.SetInput(consumer3)
 	_, err = handler.Update(ctx)
 	assert.Nil(t, err)
@@ -121,7 +125,8 @@ func TestConsumer(t *testing.T) {
 	//check update
 	input3 := &GetInput{}
 	reqBody = `{"username": "pony"}`
-	json.Unmarshal([]byte(reqBody), input3)
+	err = json.Unmarshal([]byte(reqBody), input3)
+	assert.Nil(t, err)
 	ctx.SetInput(input3)
 	ret3, err := handler.Get(ctx)
 	stored3 := ret3.(*entity.Consumer)
@@ -132,49 +137,59 @@ func TestConsumer(t *testing.T) {
 	//list page 1
 	listInput := &ListInput{}
 	reqBody = `{"page_size": 1, "page": 1}`
-	json.Unmarshal([]byte(reqBody), listInput)
+	err = json.Unmarshal([]byte(reqBody), listInput)
+	assert.Nil(t, err)
 	ctx.SetInput(listInput)
 	retPage1, err := handler.List(ctx)
+	assert.Nil(t, err)
 	dataPage1 := retPage1.(*store.ListOutput)
 	assert.Equal(t, len(dataPage1.Rows), 1)
 
 	//list page 2
 	listInput2 := &ListInput{}
 	reqBody = `{"page_size": 1, "page": 2}`
-	json.Unmarshal([]byte(reqBody), listInput2)
+	err = json.Unmarshal([]byte(reqBody), listInput2)
+	assert.Nil(t, err)
 	ctx.SetInput(listInput2)
 	retPage2, err := handler.List(ctx)
+	assert.Nil(t, err)
 	dataPage2 := retPage2.(*store.ListOutput)
 	assert.Equal(t, len(dataPage2.Rows), 1)
 
 	//list search match
 	listInput3 := &ListInput{}
 	reqBody = `{"page_size": 1, "page": 1, "username": "pony"}`
-	json.Unmarshal([]byte(reqBody), listInput3)
+	err = json.Unmarshal([]byte(reqBody), listInput3)
+	assert.Nil(t, err)
 	ctx.SetInput(listInput3)
 	retPage, err := handler.List(ctx)
+	assert.Nil(t, err)
 	dataPage := retPage.(*store.ListOutput)
 	assert.Equal(t, len(dataPage.Rows), 1)
 
 	//list search not match
 	listInput4 := &ListInput{}
 	reqBody = `{"page_size": 1, "page": 1, "username": "not-exists"}`
-	json.Unmarshal([]byte(reqBody), listInput4)
+	err = json.Unmarshal([]byte(reqBody), listInput4)
+	assert.Nil(t, err)
 	ctx.SetInput(listInput4)
 	retPage, err = handler.List(ctx)
+	assert.Nil(t, err)
 	dataPage = retPage.(*store.ListOutput)
 	assert.Equal(t, len(dataPage.Rows), 0)
 
 	//delete consumer
 	inputDel := &BatchDelete{}
 	reqBody = `{"usernames": "jack"}`
-	json.Unmarshal([]byte(reqBody), inputDel)
+	err = json.Unmarshal([]byte(reqBody), inputDel)
+	assert.Nil(t, err)
 	ctx.SetInput(inputDel)
 	_, err = handler.BatchDelete(ctx)
 	assert.Nil(t, err)
 
 	reqBody = `{"usernames": "pony"}`
-	json.Unmarshal([]byte(reqBody), inputDel)
+	err = json.Unmarshal([]byte(reqBody), inputDel)
+	assert.Nil(t, err)
 	ctx.SetInput(inputDel)
 	_, err = handler.BatchDelete(ctx)
 	assert.Nil(t, err)
@@ -192,7 +207,8 @@ func TestConsumer(t *testing.T) {
       },
     "desc": "test description"
   }`
-	json.Unmarshal([]byte(reqBody), consumer_fail)
+	err = json.Unmarshal([]byte(reqBody), consumer_fail)
+	assert.Nil(t, err)
 	ctx.SetInput(consumer_fail)
 	_, err = handler.Create(ctx)
 	assert.NotNil(t, err)
@@ -211,7 +227,8 @@ func TestConsumer(t *testing.T) {
       },
     "desc": "test description"
   }`
-	json.Unmarshal([]byte(reqBody), consumer6)
+	err = json.Unmarshal([]byte(reqBody), consumer6)
+	assert.Nil(t, err)
 	ctx.SetInput(consumer6)
 	_, err = handler.Update(ctx)
 	assert.Nil(t, err)
@@ -221,7 +238,8 @@ func TestConsumer(t *testing.T) {
 
 	//delete consumer
 	reqBody = `{"usernames": "nnn"}`
-	json.Unmarshal([]byte(reqBody), inputDel)
+	err = json.Unmarshal([]byte(reqBody), inputDel)
+	assert.Nil(t, err)
 	ctx.SetInput(inputDel)
 	_, err = handler.BatchDelete(ctx)
 	assert.Nil(t, err)
diff --git a/api/internal/handler/plugin/plugin.go b/api/internal/handler/plugin/plugin.go
index 354c562..6f46655 100644
--- a/api/internal/handler/plugin/plugin.go
+++ b/api/internal/handler/plugin/plugin.go
@@ -42,12 +42,22 @@ func (h *Handler) ApplyRoute(r *gin.Engine) {
 }
 
 type GetInput struct {
-	Name string `auto_read:"name,path" validate:"required"`
+	Name       string `auto_read:"name,path" validate:"required"`
+	SchemaType string `auto_read:"schema_type,query"`
 }
 
 func (h *Handler) Schema(c droplet.Context) (interface{}, error) {
 	input := c.Input().(*GetInput)
-	ret := conf.Schema.Get("plugins." + input.Name).Value()
+
+	var ret interface{}
+	if input.SchemaType == "consumer" {
+		ret = conf.Schema.Get("plugins." + input.Name + ".consumer_schema").Value()
+		if ret == nil {
+			ret = conf.Schema.Get("plugins." + input.Name + ".schema").Value()
+		}
+	} else {
+		ret = conf.Schema.Get("plugins." + input.Name + ".schema").Value()
+	}
 	return ret, nil
 }
 
@@ -55,7 +65,7 @@ func (h *Handler) Plugins(c droplet.Context) (interface{}, error) {
 	list := conf.Schema.Get("plugins").Map()
 
 	var ret []string
-	for pluginName, _ := range list {
+	for pluginName := range list {
 		if pluginName != "serverless-post-function" && pluginName != "serverless-pre-function" {
 			ret = append(ret, pluginName)
 		}
diff --git a/api/internal/handler/plugin/plugin_test.go b/api/internal/handler/plugin/plugin_test.go
index d962a6a..85ac9d9 100644
--- a/api/internal/handler/plugin/plugin_test.go
+++ b/api/internal/handler/plugin/plugin_test.go
@@ -40,19 +40,61 @@ func TestPlugin(t *testing.T) {
 	input := &GetInput{}
 	reqBody := `{
 	  "name": "limit-count"
-  }`
-	json.Unmarshal([]byte(reqBody), input)
+	}`
+	err = json.Unmarshal([]byte(reqBody), input)
+	assert.Nil(t, err)
 	ctx.SetInput(input)
 	val, _ := handler.Schema(ctx)
 	assert.NotNil(t, val)
 
 	//not exists
-	input2 := &GetInput{}
 	reqBody = `{
 	  "name": "not-exists"
-  }`
-	json.Unmarshal([]byte(reqBody), input2)
-	ctx.SetInput(input2)
+	}`
+	err = json.Unmarshal([]byte(reqBody), input)
+	assert.Nil(t, err)
+	ctx.SetInput(input)
 	val, _ = handler.Schema(ctx)
 	assert.Nil(t, val)
+
+	/*
+	 get plugin schema with schema_type: consumer
+	 plugin has consumer_schema
+	 return plugin`s consumer_schema
+	*/
+	reqBody = `{
+	 	"name": "jwt-auth",
+		"schema_type": "consumer"
+  	}`
+	json.Unmarshal([]byte(reqBody), input)
+	ctx.SetInput(input)
+	val, _ = handler.Schema(ctx)
+	assert.NotNil(t, val)
+
+	/*
+	 get plugin schema with schema_type: consumer
+	 plugin does not have consumer_schema
+	 return plugin`s schema
+	*/
+	reqBody = `{
+		"name": "limit-count",
+		"schema_type": "consumer"
+    }`
+	json.Unmarshal([]byte(reqBody), input)
+	ctx.SetInput(input)
+	val, _ = handler.Schema(ctx)
+	assert.NotNil(t, val)
+
+	/*
+	 get plugin schema with wrong schema_type: type,
+	 return plugin`s schema
+	*/
+	reqBody = `{
+		"name": "jwt-auth",
+		"schema_type": "type"
+  	}`
+	json.Unmarshal([]byte(reqBody), input)
+	ctx.SetInput(input)
+	val, _ = handler.Schema(ctx)
+	assert.NotNil(t, val)
 }
diff --git a/api/internal/handler/route/route.go b/api/internal/handler/route/route.go
index 5794951..bab3e9a 100644
--- a/api/internal/handler/route/route.go
+++ b/api/internal/handler/route/route.go
@@ -37,6 +37,7 @@ import (
 	"github.com/apisix/manager-api/internal/handler"
 	"github.com/apisix/manager-api/internal/utils"
 	"github.com/apisix/manager-api/internal/utils/consts"
+	"github.com/apisix/manager-api/log"
 )
 
 type Handler struct {
@@ -174,7 +175,7 @@ func generateLuaCode(script map[string]interface{}) (string, error) {
 	}
 
 	cmd := exec.Command("sh", "-c",
-		"cd "+conf.DagLibPath+" && lua cli.lua "+
+		"cd "+conf.WorkDir+"/dag-to-lua && lua cli.lua "+
 			"'"+string(scriptString)+"'")
 
 	stdout, _ := cmd.StdoutPipe()
@@ -250,6 +251,11 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
 		input.Route.ID = input.ID
 	}
 
+	if input.Route.Host != "" && len(input.Route.Hosts) > 0 {
+		return &data.SpecCodeResponse{StatusCode: http.StatusBadRequest},
+			fmt.Errorf("only one of host or hosts is allowed")
+	}
+
 	//check depend
 	if input.ServiceID != "" {
 		_, err := h.svcStore.Get(input.ServiceID)
@@ -298,6 +304,14 @@ func (h *Handler) Update(c droplet.Context) (interface{}, error) {
 				return handler.SpecCodeResponse(err), err
 			}
 		}
+	} else {
+		//remove exists script
+		script, _ := h.scriptStore.Get(input.Route.ID)
+		if script != nil {
+			if err := h.scriptStore.BatchDelete(c.Context(), strings.Split(input.Route.ID, ",")); err != nil {
+				log.Warnf("delete script %s failed", input.Route.ID)
+			}
+		}
 	}
 
 	if err := h.routeStore.Update(c.Context(), &input.Route, true); err != nil {
@@ -320,7 +334,13 @@ func (h *Handler) BatchDelete(c droplet.Context) (interface{}, error) {
 	}
 
 	//delete stored script
-	h.scriptStore.BatchDelete(c.Context(), strings.Split(input.IDs, ","))
+	if err := h.scriptStore.BatchDelete(c.Context(), strings.Split(input.IDs, ",")); err != nil {
+		//try again
+		log.Warn("try to delete script %s again", input.IDs)
+		if err := h.scriptStore.BatchDelete(c.Context(), strings.Split(input.IDs, ",")); err != nil {
+			return nil, nil
+		}
+	}
 
 	return nil, nil
 }
diff --git a/api/internal/handler/route/route_test.go b/api/internal/handler/route/route_test.go
index 3f79cd3..00a86a3 100644
--- a/api/internal/handler/route/route_test.go
+++ b/api/internal/handler/route/route_test.go
@@ -51,342 +51,343 @@ func TestRoute(t *testing.T) {
 	ctx := droplet.NewContext()
 	route := &entity.Route{}
 	reqBody := `{
-      "id": "1",
-      "name": "aaaa",
-      "uri": "/index.html",
-      "hosts": ["foo.com", "*.bar.com"],
-      "vars": [],
-      "remote_addrs": ["127.0.0.0/8"],
-      "methods": ["PUT", "GET"],
-      "upstream": {
-          "type": "roundrobin",
-          "nodes": [{
-              "host": "www.a.com",
-              "port": 80,
-              "weight": 1
-          }]
-      },
-      "script":{
-          "rule":{
-              "root":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
-              "451106f8-560c-43a4-acf2-2a6ed0ea57b8":[
-                  [
-                      "code == 403",
-                      "b93d622c-92ef-48b4-b6bb-57e1ce893ee3"
-                  ],
-                  [
-                      "",
-                      "988ef5c2-c896-4606-a666-3d4cbe24a731"
-                  ]
-              ]
-          },
-          "conf":{
-              "451106f8-560c-43a4-acf2-2a6ed0ea57b8":{
-                  "name":"uri-blocker",
-                  "conf":{
-                      "block_rules":[
-                          "root.exe",
-                          "root.m+"
-                      ],
-                      "rejected_code":403
-                  }
-              },
-              "988ef5c2-c896-4606-a666-3d4cbe24a731":{
-                  "name":"kafka-logger",
-                  "conf":{
-                      "batch_max_size":1000,
-                      "broker_list":{
-
-                      },
-                      "buffer_duration":60,
-                      "inactive_timeout":5,
-                      "include_req_body":false,
-                      "kafka_topic":"1",
-                      "key":"2",
-                      "max_retry_count":0,
-                      "name":"kafka logger",
-                      "retry_delay":1,
-                      "timeout":3
-                  }
-              },
-              "b93d622c-92ef-48b4-b6bb-57e1ce893ee3":{
-                  "name":"fault-injection",
-                  "conf":{
-                      "abort":{
-                          "body":"200",
-                          "http_status":300
-                      },
-                      "delay":{
-                          "duration":500
-                      }
-                  }
-              }
-          },
-          "chart":{
-              "hovered":{
-
-              },
-              "links":{
-                  "3a110c30-d6f3-40b1-a8ac-b828cfaa2489":{
-                      "from":{
-                          "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
-                          "portId":"port3"
-                      },
-                      "id":"3a110c30-d6f3-40b1-a8ac-b828cfaa2489",
-                      "to":{
-                          "nodeId":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
-                          "portId":"port1"
-                      }
-                  },
-                  "c1958993-c1ef-44b1-bb32-7fc6f34870c2":{
-                      "from":{
-                          "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
-                          "portId":"port2"
-                      },
-                      "id":"c1958993-c1ef-44b1-bb32-7fc6f34870c2",
-                      "to":{
-                          "nodeId":"988ef5c2-c896-4606-a666-3d4cbe24a731",
-                          "portId":"port1"
-                      }
-                  },
-                  "f9c42bf6-c8aa-4e86-8498-8dfbc5c53c23":{
-                      "from":{
-                          "nodeId":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
-                          "portId":"port2"
-                      },
-                      "id":"f9c42bf6-c8aa-4e86-8498-8dfbc5c53c23",
-                      "to":{
-                          "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
-                          "portId":"port1"
-                      }
-                  }
-              },
-              "nodes":{
-                  "3365eca3-4bc8-4769-bab3-1485dfd6a43c":{
-                      "id":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
-                      "orientation":0,
-                      "ports":{
-                          "port1":{
-                              "id":"port1",
-                              "position":{
-                                  "x":107,
-                                  "y":0
-                              },
-                              "type":"input"
-                          },
-                          "port2":{
-                              "id":"port2",
-                              "position":{
-                                  "x":92,
-                                  "y":96
-                              },
-                              "properties":{
-                                  "value":"no"
-                              },
-                              "type":"output"
-                          },
-                          "port3":{
-                              "id":"port3",
-                              "position":{
-                                  "x":122,
-                                  "y":96
-                              },
-                              "properties":{
-                                  "value":"yes"
-                              },
-                              "type":"output"
-                          }
-                      },
-                      "position":{
-                          "x":750.2627969928922,
-                          "y":301.0370335799397
-                      },
-                      "properties":{
-                          "customData":{
-                              "name":"code == 403",
-                              "type":1
-                          }
-                      },
-                      "size":{
-                          "height":96,
-                          "width":214
-                      },
-                      "type":"判断条件"
-                  },
-                  "451106f8-560c-43a4-acf2-2a6ed0ea57b8":{
-                      "id":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
-                      "orientation":0,
-                      "ports":{
-                          "port1":{
-                              "id":"port1",
-                              "position":{
-                                  "x":100,
-                                  "y":0
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"input"
-                          },
-                          "port2":{
-                              "id":"port2",
-                              "position":{
-                                  "x":100,
-                                  "y":96
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"output"
-                          }
-                      },
-                      "position":{
-                          "x":741.5684544145346,
-                          "y":126.75879247285502
-                      },
-                      "properties":{
-                          "customData":{
-                              "data":{
-                                  "block_rules":[
-                                      "root.exe",
-                                      "root.m+"
-                                  ],
-                                  "rejected_code":403
-                              },
-                              "name":"uri-blocker",
-                              "type":0
-                          }
-                      },
-                      "size":{
-                          "height":96,
-                          "width":201
-                      },
-                      "type":"uri-blocker"
-                  },
-                  "988ef5c2-c896-4606-a666-3d4cbe24a731":{
-                      "id":"988ef5c2-c896-4606-a666-3d4cbe24a731",
-                      "orientation":0,
-                      "ports":{
-                          "port1":{
-                              "id":"port1",
-                              "position":{
-                                  "x":106,
-                                  "y":0
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"input"
-                          },
-                          "port2":{
-                              "id":"port2",
-                              "position":{
-                                  "x":106,
-                                  "y":96
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"output"
-                          }
-                      },
-                      "position":{
-                          "x":607.9687500000001,
-                          "y":471.17788461538447
-                      },
-                      "properties":{
-                          "customData":{
-                              "data":{
-                                  "batch_max_size":1000,
-                                  "broker_list":{
-
-                                  },
-                                  "buffer_duration":60,
-                                  "inactive_timeout":5,
-                                  "include_req_body":false,
-                                  "kafka_topic":"1",
-                                  "key":"2",
-                                  "max_retry_count":0,
-                                  "name":"kafka logger",
-                                  "retry_delay":1,
-                                  "timeout":3
-                              },
-                              "name":"kafka-logger",
-                              "type":0
-                          }
-                      },
-                      "size":{
-                          "height":96,
-                          "width":212
-                      },
-                      "type":"kafka-logger"
-                  },
-                  "b93d622c-92ef-48b4-b6bb-57e1ce893ee3":{
-                      "id":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
-                      "orientation":0,
-                      "ports":{
-                          "port1":{
-                              "id":"port1",
-                              "position":{
-                                  "x":110,
-                                  "y":0
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"input"
-                          },
-                          "port2":{
-                              "id":"port2",
-                              "position":{
-                                  "x":110,
-                                  "y":96
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"output"
-                          }
-                      },
-                      "position":{
-                          "x":988.9074986362261,
-                          "y":478.62041800736495
-                      },
-                      "properties":{
-                          "customData":{
-                              "data":{
-                                  "abort":{
-                                      "body":"200",
-                                      "http_status":300
-                                  },
-                                  "delay":{
-                                      "duration":500
-                                  }
-                              },
-                              "name":"fault-injection",
-                              "type":0
-                          }
-                      },
-                      "size":{
-                          "height":96,
-                          "width":219
-                      },
-                      "type":"fault-injection"
-                  }
-              },
-              "offset":{
-                  "x":-376.83,
-                  "y":87.98
-              },
-              "scale":0.832,
-              "selected":{
-                  "id":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
-                  "type":"node"
-              }
-          }
-      }
-  }`
-	json.Unmarshal([]byte(reqBody), route)
+	  "id": "1",
+	  "name": "aaaa",
+	  "uri": "/index.html",
+	  "hosts": ["foo.com", "*.bar.com"],
+	  "vars": [],
+	  "remote_addrs": ["127.0.0.0/8"],
+	  "methods": ["PUT", "GET"],
+	  "upstream": {
+	      "type": "roundrobin",
+	      "nodes": [{
+	          "host": "www.a.com",
+	          "port": 80,
+	          "weight": 1
+	      }]
+	  },
+	  "script":{
+	      "rule":{
+	          "root":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
+	          "451106f8-560c-43a4-acf2-2a6ed0ea57b8":[
+	              [
+	                  "code == 403",
+	                  "b93d622c-92ef-48b4-b6bb-57e1ce893ee3"
+	              ],
+	              [
+	                  "",
+	                  "988ef5c2-c896-4606-a666-3d4cbe24a731"
+	              ]
+	          ]
+	      },
+	      "conf":{
+	          "451106f8-560c-43a4-acf2-2a6ed0ea57b8":{
+	              "name":"uri-blocker",
+	              "conf":{
+	                  "block_rules":[
+	                      "root.exe",
+	                      "root.m+"
+	                  ],
+	                  "rejected_code":403
+	              }
+	          },
+	          "988ef5c2-c896-4606-a666-3d4cbe24a731":{
+	              "name":"kafka-logger",
+	              "conf":{
+	                  "batch_max_size":1000,
+	                  "broker_list":{
+	
+	                  },
+	                  "buffer_duration":60,
+	                  "inactive_timeout":5,
+	                  "include_req_body":false,
+	                  "kafka_topic":"1",
+	                  "key":"2",
+	                  "max_retry_count":0,
+	                  "name":"kafka logger",
+	                  "retry_delay":1,
+	                  "timeout":3
+	              }
+	          },
+	          "b93d622c-92ef-48b4-b6bb-57e1ce893ee3":{
+	              "name":"fault-injection",
+	              "conf":{
+	                  "abort":{
+	                      "body":"200",
+	                      "http_status":300
+	                  },
+	                  "delay":{
+	                      "duration":500
+	                  }
+	              }
+	          }
+	      },
+	      "chart":{
+	          "hovered":{
+	
+	          },
+	          "links":{
+	              "3a110c30-d6f3-40b1-a8ac-b828cfaa2489":{
+	                  "from":{
+	                      "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
+	                      "portId":"port3"
+	                  },
+	                  "id":"3a110c30-d6f3-40b1-a8ac-b828cfaa2489",
+	                  "to":{
+	                      "nodeId":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
+	                      "portId":"port1"
+	                  }
+	              },
+	              "c1958993-c1ef-44b1-bb32-7fc6f34870c2":{
+	                  "from":{
+	                      "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
+	                      "portId":"port2"
+	                  },
+	                  "id":"c1958993-c1ef-44b1-bb32-7fc6f34870c2",
+	                  "to":{
+	                      "nodeId":"988ef5c2-c896-4606-a666-3d4cbe24a731",
+	                      "portId":"port1"
+	                  }
+	              },
+	              "f9c42bf6-c8aa-4e86-8498-8dfbc5c53c23":{
+	                  "from":{
+	                      "nodeId":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
+	                      "portId":"port2"
+	                  },
+	                  "id":"f9c42bf6-c8aa-4e86-8498-8dfbc5c53c23",
+	                  "to":{
+	                      "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
+	                      "portId":"port1"
+	                  }
+	              }
+	          },
+	          "nodes":{
+	              "3365eca3-4bc8-4769-bab3-1485dfd6a43c":{
+	                  "id":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
+	                  "orientation":0,
+	                  "ports":{
+	                      "port1":{
+	                          "id":"port1",
+	                          "position":{
+	                              "x":107,
+	                              "y":0
+	                          },
+	                          "type":"input"
+	                      },
+	                      "port2":{
+	                          "id":"port2",
+	                          "position":{
+	                              "x":92,
+	                              "y":96
+	                          },
+	                          "properties":{
+	                              "value":"no"
+	                          },
+	                          "type":"output"
+	                      },
+	                      "port3":{
+	                          "id":"port3",
+	                          "position":{
+	                              "x":122,
+	                              "y":96
+	                          },
+	                          "properties":{
+	                              "value":"yes"
+	                          },
+	                          "type":"output"
+	                      }
+	                  },
+	                  "position":{
+	                      "x":750.2627969928922,
+	                      "y":301.0370335799397
+	                  },
+	                  "properties":{
+	                      "customData":{
+	                          "name":"code == 403",
+	                          "type":1
+	                      }
+	                  },
+	                  "size":{
+	                      "height":96,
+	                      "width":214
+	                  },
+	                  "type":"判断条件"
+	              },
+	              "451106f8-560c-43a4-acf2-2a6ed0ea57b8":{
+	                  "id":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
+	                  "orientation":0,
+	                  "ports":{
+	                      "port1":{
+	                          "id":"port1",
+	                          "position":{
+	                              "x":100,
+	                              "y":0
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"input"
+	                      },
+	                      "port2":{
+	                          "id":"port2",
+	                          "position":{
+	                              "x":100,
+	                              "y":96
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"output"
+	                      }
+	                  },
+	                  "position":{
+	                      "x":741.5684544145346,
+	                      "y":126.75879247285502
+	                  },
+	                  "properties":{
+	                      "customData":{
+	                          "data":{
+	                              "block_rules":[
+	                                  "root.exe",
+	                                  "root.m+"
+	                              ],
+	                              "rejected_code":403
+	                          },
+	                          "name":"uri-blocker",
+	                          "type":0
+	                      }
+	                  },
+	                  "size":{
+	                      "height":96,
+	                      "width":201
+	                  },
+	                  "type":"uri-blocker"
+	              },
+	              "988ef5c2-c896-4606-a666-3d4cbe24a731":{
+	                  "id":"988ef5c2-c896-4606-a666-3d4cbe24a731",
+	                  "orientation":0,
+	                  "ports":{
+	                      "port1":{
+	                          "id":"port1",
+	                          "position":{
+	                              "x":106,
+	                              "y":0
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"input"
+	                      },
+	                      "port2":{
+	                          "id":"port2",
+	                          "position":{
+	                              "x":106,
+	                              "y":96
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"output"
+	                      }
+	                  },
+	                  "position":{
+	                      "x":607.9687500000001,
+	                      "y":471.17788461538447
+	                  },
+	                  "properties":{
+	                      "customData":{
+	                          "data":{
+	                              "batch_max_size":1000,
+	                              "broker_list":{
+	
+	                              },
+	                              "buffer_duration":60,
+	                              "inactive_timeout":5,
+	                              "include_req_body":false,
+	                              "kafka_topic":"1",
+	                              "key":"2",
+	                              "max_retry_count":0,
+	                              "name":"kafka logger",
+	                              "retry_delay":1,
+	                              "timeout":3
+	                          },
+	                          "name":"kafka-logger",
+	                          "type":0
+	                      }
+	                  },
+	                  "size":{
+	                      "height":96,
+	                      "width":212
+	                  },
+	                  "type":"kafka-logger"
+	              },
+	              "b93d622c-92ef-48b4-b6bb-57e1ce893ee3":{
+	                  "id":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
+	                  "orientation":0,
+	                  "ports":{
+	                      "port1":{
+	                          "id":"port1",
+	                          "position":{
+	                              "x":110,
+	                              "y":0
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"input"
+	                      },
+	                      "port2":{
+	                          "id":"port2",
+	                          "position":{
+	                              "x":110,
+	                              "y":96
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"output"
+	                      }
+	                  },
+	                  "position":{
+	                      "x":988.9074986362261,
+	                      "y":478.62041800736495
+	                  },
+	                  "properties":{
+	                      "customData":{
+	                          "data":{
+	                              "abort":{
+	                                  "body":"200",
+	                                  "http_status":300
+	                              },
+	                              "delay":{
+	                                  "duration":500
+	                              }
+	                          },
+	                          "name":"fault-injection",
+	                          "type":0
+	                      }
+	                  },
+	                  "size":{
+	                      "height":96,
+	                      "width":219
+	                  },
+	                  "type":"fault-injection"
+	              }
+	          },
+	          "offset":{
+	              "x":-376.83,
+	              "y":87.98
+	          },
+	          "scale":0.832,
+	          "selected":{
+	              "id":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
+	              "type":"node"
+	          }
+	      }
+	  }
+	}`
+	err = json.Unmarshal([]byte(reqBody), route)
+	assert.Nil(t, err)
 	ctx.SetInput(route)
 	_, err = handler.Create(ctx)
 	assert.Nil(t, err)
@@ -407,342 +408,343 @@ func TestRoute(t *testing.T) {
 	route2 := &UpdateInput{}
 	route2.ID = "1"
 	reqBody = `{
-      "id": "1",
-      "name": "aaaa",
-      "uri": "/index.html",
-      "hosts": ["foo.com", "*.bar.com"],
-      "remote_addrs": ["127.0.0.0/8"],
-      "methods": ["PUT", "GET"],
-      "upstream": {
-          "type": "roundrobin",
-          "nodes": [{
-              "host": "www.a.com",
-              "port": 80,
-              "weight": 1
-          }]
-      },
-      "script":{
-          "rule":{
-              "root":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
-              "451106f8-560c-43a4-acf2-2a6ed0ea57b8":[
-                  [
-                      "code == 403",
-                      "b93d622c-92ef-48b4-b6bb-57e1ce893ee3"
-                  ],
-                  [
-                      "",
-                      "988ef5c2-c896-4606-a666-3d4cbe24a731"
-                  ]
-              ]
-          },
-          "conf":{
-              "451106f8-560c-43a4-acf2-2a6ed0ea57b8":{
-                  "name":"uri-blocker",
-                  "conf":{
-                      "block_rules":[
-                          "root.exe",
-                          "root.m+"
-                      ],
-                      "rejected_code":403
-                  }
-              },
-              "988ef5c2-c896-4606-a666-3d4cbe24a731":{
-                  "name":"kafka-logger",
-                  "conf":{
-                      "batch_max_size":1000,
-                      "broker_list":{
-
-                      },
-                      "buffer_duration":60,
-                      "inactive_timeout":5,
-                      "include_req_body":false,
-                      "kafka_topic":"1",
-                      "key":"2",
-                      "max_retry_count":0,
-                      "name":"kafka logger",
-                      "retry_delay":1,
-                      "timeout":3
-                  }
-              },
-              "b93d622c-92ef-48b4-b6bb-57e1ce893ee3":{
-                  "name":"fault-injection",
-                  "conf":{
-                      "abort":{
-                          "body":"200",
-                          "http_status":300
-                      },
-                      "delay":{
-                          "duration":500
-                      }
-                  }
-              }
-          },
-          "chart":{
-              "hovered":{
-
-              },
-              "links":{
-                  "3a110c30-d6f3-40b1-a8ac-b828cfaa2489":{
-                      "from":{
-                          "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
-                          "portId":"port3"
-                      },
-                      "id":"3a110c30-d6f3-40b1-a8ac-b828cfaa2489",
-                      "to":{
-                          "nodeId":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
-                          "portId":"port1"
-                      }
-                  },
-                  "c1958993-c1ef-44b1-bb32-7fc6f34870c2":{
-                      "from":{
-                          "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
-                          "portId":"port2"
-                      },
-                      "id":"c1958993-c1ef-44b1-bb32-7fc6f34870c2",
-                      "to":{
-                          "nodeId":"988ef5c2-c896-4606-a666-3d4cbe24a731",
-                          "portId":"port1"
-                      }
-                  },
-                  "f9c42bf6-c8aa-4e86-8498-8dfbc5c53c23":{
-                      "from":{
-                          "nodeId":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
-                          "portId":"port2"
-                      },
-                      "id":"f9c42bf6-c8aa-4e86-8498-8dfbc5c53c23",
-                      "to":{
-                          "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
-                          "portId":"port1"
-                      }
-                  }
-              },
-              "nodes":{
-                  "3365eca3-4bc8-4769-bab3-1485dfd6a43c":{
-                      "id":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
-                      "orientation":0,
-                      "ports":{
-                          "port1":{
-                              "id":"port1",
-                              "position":{
-                                  "x":107,
-                                  "y":0
-                              },
-                              "type":"input"
-                          },
-                          "port2":{
-                              "id":"port2",
-                              "position":{
-                                  "x":92,
-                                  "y":96
-                              },
-                              "properties":{
-                                  "value":"no"
-                              },
-                              "type":"output"
-                          },
-                          "port3":{
-                              "id":"port3",
-                              "position":{
-                                  "x":122,
-                                  "y":96
-                              },
-                              "properties":{
-                                  "value":"yes"
-                              },
-                              "type":"output"
-                          }
-                      },
-                      "position":{
-                          "x":750.2627969928922,
-                          "y":301.0370335799397
-                      },
-                      "properties":{
-                          "customData":{
-                              "name":"code == 403",
-                              "type":1
-                          }
-                      },
-                      "size":{
-                          "height":96,
-                          "width":214
-                      },
-                      "type":"判断条件"
-                  },
-                  "451106f8-560c-43a4-acf2-2a6ed0ea57b8":{
-                      "id":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
-                      "orientation":0,
-                      "ports":{
-                          "port1":{
-                              "id":"port1",
-                              "position":{
-                                  "x":100,
-                                  "y":0
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"input"
-                          },
-                          "port2":{
-                              "id":"port2",
-                              "position":{
-                                  "x":100,
-                                  "y":96
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"output"
-                          }
-                      },
-                      "position":{
-                          "x":741.5684544145346,
-                          "y":126.75879247285502
-                      },
-                      "properties":{
-                          "customData":{
-                              "data":{
-                                  "block_rules":[
-                                      "root.exe",
-                                      "root.m+"
-                                  ],
-                                  "rejected_code":403
-                              },
-                              "name":"uri-blocker",
-                              "type":0
-                          }
-                      },
-                      "size":{
-                          "height":96,
-                          "width":201
-                      },
-                      "type":"uri-blocker"
-                  },
-                  "988ef5c2-c896-4606-a666-3d4cbe24a731":{
-                      "id":"988ef5c2-c896-4606-a666-3d4cbe24a731",
-                      "orientation":0,
-                      "ports":{
-                          "port1":{
-                              "id":"port1",
-                              "position":{
-                                  "x":106,
-                                  "y":0
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"input"
-                          },
-                          "port2":{
-                              "id":"port2",
-                              "position":{
-                                  "x":106,
-                                  "y":96
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"output"
-                          }
-                      },
-                      "position":{
-                          "x":607.9687500000001,
-                          "y":471.17788461538447
-                      },
-                      "properties":{
-                          "customData":{
-                              "data":{
-                                  "batch_max_size":1000,
-                                  "broker_list":{
-
-                                  },
-                                  "buffer_duration":60,
-                                  "inactive_timeout":5,
-                                  "include_req_body":false,
-                                  "kafka_topic":"1",
-                                  "key":"2",
-                                  "max_retry_count":0,
-                                  "name":"kafka logger",
-                                  "retry_delay":1,
-                                  "timeout":3
-                              },
-                              "name":"kafka-logger",
-                              "type":0
-                          }
-                      },
-                      "size":{
-                          "height":96,
-                          "width":212
-                      },
-                      "type":"kafka-logger"
-                  },
-                  "b93d622c-92ef-48b4-b6bb-57e1ce893ee3":{
-                      "id":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
-                      "orientation":0,
-                      "ports":{
-                          "port1":{
-                              "id":"port1",
-                              "position":{
-                                  "x":110,
-                                  "y":0
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"input"
-                          },
-                          "port2":{
-                              "id":"port2",
-                              "position":{
-                                  "x":110,
-                                  "y":96
-                              },
-                              "properties":{
-                                  "custom":"property"
-                              },
-                              "type":"output"
-                          }
-                      },
-                      "position":{
-                          "x":988.9074986362261,
-                          "y":478.62041800736495
-                      },
-                      "properties":{
-                          "customData":{
-                              "data":{
-                                  "abort":{
-                                      "body":"200",
-                                      "http_status":300
-                                  },
-                                  "delay":{
-                                      "duration":500
-                                  }
-                              },
-                              "name":"fault-injection",
-                              "type":0
-                          }
-                      },
-                      "size":{
-                          "height":96,
-                          "width":219
-                      },
-                      "type":"fault-injection"
-                  }
-              },
-              "offset":{
-                  "x":-376.83,
-                  "y":87.98
-              },
-              "scale":0.832,
-              "selected":{
-                  "id":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
-                  "type":"node"
-              }
-          }
-      }
-  }`
-
-	json.Unmarshal([]byte(reqBody), route2)
+	  "id": "1",
+	  "name": "aaaa",
+	  "uri": "/index.html",
+	  "hosts": ["foo.com", "*.bar.com"],
+	  "remote_addrs": ["127.0.0.0/8"],
+	  "methods": ["PUT", "GET"],
+	  "upstream": {
+	      "type": "roundrobin",
+	      "nodes": [{
+	          "host": "www.a.com",
+	          "port": 80,
+	          "weight": 1
+	      }]
+	  },
+	  "script":{
+	      "rule":{
+	          "root":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
+	          "451106f8-560c-43a4-acf2-2a6ed0ea57b8":[
+	              [
+	                  "code == 403",
+	                  "b93d622c-92ef-48b4-b6bb-57e1ce893ee3"
+	              ],
+	              [
+	                  "",
+	                  "988ef5c2-c896-4606-a666-3d4cbe24a731"
+	              ]
+	          ]
+	      },
+	      "conf":{
+	          "451106f8-560c-43a4-acf2-2a6ed0ea57b8":{
+	              "name":"uri-blocker",
+	              "conf":{
+	                  "block_rules":[
+	                      "root.exe",
+	                      "root.m+"
+	                  ],
+	                  "rejected_code":403
+	              }
+	          },
+	          "988ef5c2-c896-4606-a666-3d4cbe24a731":{
+	              "name":"kafka-logger",
+	              "conf":{
+	                  "batch_max_size":1000,
+	                  "broker_list":{
+	
+	                  },
+	                  "buffer_duration":60,
+	                  "inactive_timeout":5,
+	                  "include_req_body":false,
+	                  "kafka_topic":"1",
+	                  "key":"2",
+	                  "max_retry_count":0,
+	                  "name":"kafka logger",
+	                  "retry_delay":1,
+	                  "timeout":3
+	              }
+	          },
+	          "b93d622c-92ef-48b4-b6bb-57e1ce893ee3":{
+	              "name":"fault-injection",
+	              "conf":{
+	                  "abort":{
+	                      "body":"200",
+	                      "http_status":300
+	                  },
+	                  "delay":{
+	                      "duration":500
+	                  }
+	              }
+	          }
+	      },
+	      "chart":{
+	          "hovered":{
+	
+	          },
+	          "links":{
+	              "3a110c30-d6f3-40b1-a8ac-b828cfaa2489":{
+	                  "from":{
+	                      "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
+	                      "portId":"port3"
+	                  },
+	                  "id":"3a110c30-d6f3-40b1-a8ac-b828cfaa2489",
+	                  "to":{
+	                      "nodeId":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
+	                      "portId":"port1"
+	                  }
+	              },
+	              "c1958993-c1ef-44b1-bb32-7fc6f34870c2":{
+	                  "from":{
+	                      "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
+	                      "portId":"port2"
+	                  },
+	                  "id":"c1958993-c1ef-44b1-bb32-7fc6f34870c2",
+	                  "to":{
+	                      "nodeId":"988ef5c2-c896-4606-a666-3d4cbe24a731",
+	                      "portId":"port1"
+	                  }
+	              },
+	              "f9c42bf6-c8aa-4e86-8498-8dfbc5c53c23":{
+	                  "from":{
+	                      "nodeId":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
+	                      "portId":"port2"
+	                  },
+	                  "id":"f9c42bf6-c8aa-4e86-8498-8dfbc5c53c23",
+	                  "to":{
+	                      "nodeId":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
+	                      "portId":"port1"
+	                  }
+	              }
+	          },
+	          "nodes":{
+	              "3365eca3-4bc8-4769-bab3-1485dfd6a43c":{
+	                  "id":"3365eca3-4bc8-4769-bab3-1485dfd6a43c",
+	                  "orientation":0,
+	                  "ports":{
+	                      "port1":{
+	                          "id":"port1",
+	                          "position":{
+	                              "x":107,
+	                              "y":0
+	                          },
+	                          "type":"input"
+	                      },
+	                      "port2":{
+	                          "id":"port2",
+	                          "position":{
+	                              "x":92,
+	                              "y":96
+	                          },
+	                          "properties":{
+	                              "value":"no"
+	                          },
+	                          "type":"output"
+	                      },
+	                      "port3":{
+	                          "id":"port3",
+	                          "position":{
+	                              "x":122,
+	                              "y":96
+	                          },
+	                          "properties":{
+	                              "value":"yes"
+	                          },
+	                          "type":"output"
+	                      }
+	                  },
+	                  "position":{
+	                      "x":750.2627969928922,
+	                      "y":301.0370335799397
+	                  },
+	                  "properties":{
+	                      "customData":{
+	                          "name":"code == 403",
+	                          "type":1
+	                      }
+	                  },
+	                  "size":{
+	                      "height":96,
+	                      "width":214
+	                  },
+	                  "type":"判断条件"
+	              },
+	              "451106f8-560c-43a4-acf2-2a6ed0ea57b8":{
+	                  "id":"451106f8-560c-43a4-acf2-2a6ed0ea57b8",
+	                  "orientation":0,
+	                  "ports":{
+	                      "port1":{
+	                          "id":"port1",
+	                          "position":{
+	                              "x":100,
+	                              "y":0
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"input"
+	                      },
+	                      "port2":{
+	                          "id":"port2",
+	                          "position":{
+	                              "x":100,
+	                              "y":96
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"output"
+	                      }
+	                  },
+	                  "position":{
+	                      "x":741.5684544145346,
+	                      "y":126.75879247285502
+	                  },
+	                  "properties":{
+	                      "customData":{
+	                          "data":{
+	                              "block_rules":[
+	                                  "root.exe",
+	                                  "root.m+"
+	                              ],
+	                              "rejected_code":403
+	                          },
+	                          "name":"uri-blocker",
+	                          "type":0
+	                      }
+	                  },
+	                  "size":{
+	                      "height":96,
+	                      "width":201
+	                  },
+	                  "type":"uri-blocker"
+	              },
+	              "988ef5c2-c896-4606-a666-3d4cbe24a731":{
+	                  "id":"988ef5c2-c896-4606-a666-3d4cbe24a731",
+	                  "orientation":0,
+	                  "ports":{
+	                      "port1":{
+	                          "id":"port1",
+	                          "position":{
+	                              "x":106,
+	                              "y":0
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"input"
+	                      },
+	                      "port2":{
+	                          "id":"port2",
+	                          "position":{
+	                              "x":106,
+	                              "y":96
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"output"
+	                      }
+	                  },
+	                  "position":{
+	                      "x":607.9687500000001,
+	                      "y":471.17788461538447
+	                  },
+	                  "properties":{
+	                      "customData":{
+	                          "data":{
+	                              "batch_max_size":1000,
+	                              "broker_list":{
+	
+	                              },
+	                              "buffer_duration":60,
+	                              "inactive_timeout":5,
+	                              "include_req_body":false,
+	                              "kafka_topic":"1",
+	                              "key":"2",
+	                              "max_retry_count":0,
+	                              "name":"kafka logger",
+	                              "retry_delay":1,
+	                              "timeout":3
+	                          },
+	                          "name":"kafka-logger",
+	                          "type":0
+	                      }
+	                  },
+	                  "size":{
+	                      "height":96,
+	                      "width":212
+	                  },
+	                  "type":"kafka-logger"
+	              },
+	              "b93d622c-92ef-48b4-b6bb-57e1ce893ee3":{
+	                  "id":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
+	                  "orientation":0,
+	                  "ports":{
+	                      "port1":{
+	                          "id":"port1",
+	                          "position":{
+	                              "x":110,
+	                              "y":0
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"input"
+	                      },
+	                      "port2":{
+	                          "id":"port2",
+	                          "position":{
+	                              "x":110,
+	                              "y":96
+	                          },
+	                          "properties":{
+	                              "custom":"property"
+	                          },
+	                          "type":"output"
+	                      }
+	                  },
+	                  "position":{
+	                      "x":988.9074986362261,
+	                      "y":478.62041800736495
+	                  },
+	                  "properties":{
+	                      "customData":{
+	                          "data":{
+	                              "abort":{
+	                                  "body":"200",
+	                                  "http_status":300
+	                              },
+	                              "delay":{
+	                                  "duration":500
+	                              }
+	                          },
+	                          "name":"fault-injection",
+	                          "type":0
+	                      }
+	                  },
+	                  "size":{
+	                      "height":96,
+	                      "width":219
+	                  },
+	                  "type":"fault-injection"
+	              }
+	          },
+	          "offset":{
+	              "x":-376.83,
+	              "y":87.98
+	          },
+	          "scale":0.832,
+	          "selected":{
+	              "id":"b93d622c-92ef-48b4-b6bb-57e1ce893ee3",
+	              "type":"node"
+	          }
+	      }
+	  }
+	}`
+
+	err = json.Unmarshal([]byte(reqBody), route2)
+	assert.Nil(t, err)
 	ctx.SetInput(route2)
 	_, err = handler.Update(ctx)
 	assert.Nil(t, err)
@@ -753,7 +755,8 @@ func TestRoute(t *testing.T) {
 	//list
 	listInput := &ListInput{}
 	reqBody = `{"page_size": 1, "page": 1}`
-	json.Unmarshal([]byte(reqBody), listInput)
+	err = json.Unmarshal([]byte(reqBody), listInput)
+	assert.Nil(t, err)
 	ctx.SetInput(listInput)
 	retPage, err := handler.List(ctx)
 	assert.Nil(t, err)
@@ -763,7 +766,8 @@ func TestRoute(t *testing.T) {
 	//list search match
 	listInput2 := &ListInput{}
 	reqBody = `{"page_size": 1, "page": 1, "name": "a", "uri": "index"}`
-	json.Unmarshal([]byte(reqBody), listInput2)
+	err = json.Unmarshal([]byte(reqBody), listInput2)
+	assert.Nil(t, err)
 	ctx.SetInput(listInput2)
 	retPage, err = handler.List(ctx)
 	assert.Nil(t, err)
@@ -773,7 +777,8 @@ func TestRoute(t *testing.T) {
 	//list search name not match
 	listInput3 := &ListInput{}
 	reqBody = `{"page_size": 1, "page": 1, "name": "not-exists", "uri": "index"}`
-	json.Unmarshal([]byte(reqBody), listInput3)
+	err = json.Unmarshal([]byte(reqBody), listInput3)
+	assert.Nil(t, err)
 	ctx.SetInput(listInput3)
 	retPage, err = handler.List(ctx)
 	assert.Nil(t, err)
@@ -783,7 +788,8 @@ func TestRoute(t *testing.T) {
 	//list search uri not match
 	listInput4 := &ListInput{}
 	reqBody = `{"page_size": 1, "page": 1, "name": "a", "uri": "not-exists"}`
-	json.Unmarshal([]byte(reqBody), listInput4)
+	err = json.Unmarshal([]byte(reqBody), listInput4)
+	assert.Nil(t, err)
 	ctx.SetInput(listInput4)
 	retPage, err = handler.List(ctx)
 	assert.Nil(t, err)
@@ -793,18 +799,19 @@ func TestRoute(t *testing.T) {
 	//create route using uris
 	route3 := &entity.Route{}
 	reqBody = `{
-      "id": "2",
-      "name": "bbbbb",
-      "uris": ["/aa", "/bb"],
-      "hosts": ["foo.com", "*.bar.com"],
-      "remote_addrs": ["127.0.0.0/8"],
-      "methods": ["PUT", "GET"],
-      "upstream": {
-          "type": "roundrobin",
-          "nodes": {"www.a.com:80": 1}
-      }
-  }`
-	json.Unmarshal([]byte(reqBody), route3)
+	  "id": "2",
+	  "name": "bbbbb",
+	  "uris": ["/aa", "/bb"],
+	  "hosts": ["foo.com", "*.bar.com"],
+	  "remote_addrs": ["127.0.0.0/8"],
+	  "methods": ["PUT", "GET"],
+	  "upstream": {
+	      "type": "roundrobin",
+	      "nodes": {"www.a.com:80": 1}
+	  }
+	}`
+	err = json.Unmarshal([]byte(reqBody), route3)
+	assert.Nil(t, err)
 	ctx.SetInput(route3)
 	_, err = handler.Create(ctx)
 	assert.Nil(t, err)
@@ -815,7 +822,8 @@ func TestRoute(t *testing.T) {
 	//list search match uris
 	listInput5 := &ListInput{}
 	reqBody = `{"page_size": 1, "page": 1, "name": "bbb", "uri": "bb"}`
-	json.Unmarshal([]byte(reqBody), listInput5)
+	err = json.Unmarshal([]byte(reqBody), listInput5)
+	assert.Nil(t, err)
 	ctx.SetInput(listInput5)
 	retPage, err = handler.List(ctx)
 	assert.Nil(t, err)
@@ -825,7 +833,8 @@ func TestRoute(t *testing.T) {
 	//delete test data
 	inputDel := &BatchDelete{}
 	reqBody = `{"ids": "1,2"}`
-	json.Unmarshal([]byte(reqBody), inputDel)
+	err = json.Unmarshal([]byte(reqBody), inputDel)
+	assert.Nil(t, err)
 	ctx.SetInput(inputDel)
 	_, err = handler.BatchDelete(ctx)
 	assert.Nil(t, err)
@@ -836,7 +845,8 @@ func TestRoute(t *testing.T) {
 	//get route -- deleted, not found
 	getInput := &GetInput{}
 	reqBody = `{"id": "1"}`
-	json.Unmarshal([]byte(reqBody), getInput)
+	err = json.Unmarshal([]byte(reqBody), getInput)
+	assert.Nil(t, err)
 	ctx.SetInput(getInput)
 	ret, err = handler.Get(ctx)
 	assert.EqualError(t, err, "data not found")
@@ -844,7 +854,8 @@ func TestRoute(t *testing.T) {
 
 	//delete test data
 	reqBody = `{"ids": "not-exists"}`
-	json.Unmarshal([]byte(reqBody), inputDel)
+	err = json.Unmarshal([]byte(reqBody), inputDel)
+	assert.Nil(t, err)
 	ctx.SetInput(inputDel)
 	ret, err = handler.BatchDelete(ctx)
 	assert.NotNil(t, err)
@@ -853,12 +864,13 @@ func TestRoute(t *testing.T) {
 	//create route with not exist upstream id
 	route4 := &entity.Route{}
 	reqBody = `{
-      "id": "2222",
-      "name": "r222",
-      "uris": ["/aa", "/bb"],
-      "upstream_id": "not-exists"
-  }`
-	json.Unmarshal([]byte(reqBody), route4)
+	  "id": "2222",
+	  "name": "r222",
+	  "uris": ["/aa", "/bb"],
+	  "upstream_id": "not-exists"
+	}`
+	err = json.Unmarshal([]byte(reqBody), route4)
+	assert.Nil(t, err)
 	ctx.SetInput(route4)
 	ret, err = handler.Create(ctx)
 	assert.NotNil(t, err)
@@ -867,20 +879,21 @@ func TestRoute(t *testing.T) {
 	//type:chash, hash_on: vars, wrong key
 	route5 := &entity.Route{}
 	reqBody = `{
-      "id": "1",
-      "methods": ["GET"],
-      "upstream": {
-          "nodes": {
-              "127.0.0.1:8080": 1
-          },
-          "type": "chash",
-          "hash_on":"vars",
-          "key": "not_support"
-      },
-      "desc": "new route",
-      "uri": "/index.html"
-  }`
-	json.Unmarshal([]byte(reqBody), route5)
+	  "id": "1",
+	  "methods": ["GET"],
+	  "upstream": {
+	      "nodes": {
+	          "127.0.0.1:8080": 1
+	      },
+	      "type": "chash",
+	      "hash_on":"vars",
+	      "key": "not_support"
+	  },
+	  "desc": "new route",
+	  "uri": "/index.html"
+	}`
+	err = json.Unmarshal([]byte(reqBody), route5)
+	assert.Nil(t, err)
 	ctx.SetInput(route5)
 	ret, err = handler.Create(ctx)
 	assert.NotNil(t, err)
@@ -889,19 +902,20 @@ func TestRoute(t *testing.T) {
 	//type:chash, hash_on: cookie, missing key
 	route6 := &entity.Route{}
 	reqBody = `{
-      "id": "1",
-      "methods": ["GET"],
-      "upstream": {
-          "nodes": {
-              "127.0.0.1:8080": 1
-          },
-          "type": "chash",
-          "hash_on":"cookie"
-      },
-      "desc": "new route",
-      "uri": "/index.html"
-  }`
-	json.Unmarshal([]byte(reqBody), route6)
+	  "id": "1",
+	  "methods": ["GET"],
+	  "upstream": {
+	      "nodes": {
+	          "127.0.0.1:8080": 1
+	      },
+	      "type": "chash",
+	      "hash_on":"cookie"
+	  },
+	  "desc": "new route",
+	  "uri": "/index.html"
+	}`
+	err = json.Unmarshal([]byte(reqBody), route6)
+	assert.Nil(t, err)
 	ctx.SetInput(route6)
 	ret, err = handler.Create(ctx)
 	assert.NotNil(t, err)
@@ -910,22 +924,23 @@ func TestRoute(t *testing.T) {
 	//create route with out upstream
 	route11 := &entity.Route{}
 	reqBody = `{
-      "id": "11",
-      "name": "bbbbb",
-      "uri": "/r11",
-      "hosts": ["foo.com", "*.bar.com"],
-      "remote_addrs": ["127.0.0.0/8"],
-      "methods": ["PUT", "GET"],
-      "plugins": {
-          "limit-count": {
-              "count": 2,
-              "time_window": 60,
-              "rejected_code": 503,
-              "key": "remote_addr"
-          }
-      }
-  }`
-	json.Unmarshal([]byte(reqBody), route11)
+	  "id": "11",
+	  "name": "bbbbb",
+	  "uri": "/r11",
+	  "hosts": ["foo.com", "*.bar.com"],
... 8144 lines suppressed ...