add config management #242
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/Constants.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/Constants.java
index 86d9d2b..44febab 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/Constants.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/common/util/Constants.java
@@ -29,6 +29,7 @@
     public static final String PATH_SEPARATOR = "/";
     public static final String GROUP_KEY = "group";
     public static final String CONFIG_KEY = "config";
+    public static final String DUBBO_PROPERTY = "dubbo.properties";
     public static final String PROVIDER_SIDE = "provider";
     public static final String CONSUMER_SIDE = "consumer";
     public static final String CATEGORY_KEY = "category";
@@ -52,6 +53,7 @@
     public static final String PROVIDERS_CATEGORY = "providers";
     public static final String CONSUMERS_CATEGORY = "consumers";
     public static final String SPECIFICATION_VERSION_KEY = "specVersion";
+    public static final String GLOBAL_CONFIG = "global";
     public static final Set<String> CONFIGS = new HashSet<>();
 
     static {
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java
index a9c088d..1c6b4bd 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java
@@ -23,12 +23,14 @@
 import org.apache.dubbo.admin.registry.config.GovernanceConfiguration;
 import org.apache.dubbo.admin.registry.metadata.MetaDataCollector;
 import org.apache.dubbo.admin.registry.metadata.impl.NoOpMetadataCollector;
+import org.apache.dubbo.admin.service.ManagementService;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.registry.Registry;
 import org.apache.dubbo.registry.RegistryFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -62,6 +64,7 @@
     private URL metadataUrl;
 
 
+
     /*
      * generate dynamic configuration client
      */
@@ -79,8 +82,10 @@
             if (StringUtils.isNotEmpty(config)) {
                 Arrays.stream(config.split("\n")).forEach( s -> {
                     if(s.startsWith(Constants.REGISTRY_ADDRESS)) {
-                        registryUrl = formUrl(s.split("=")[1].trim(), group, username, password);
+                        String registryAddress = s.split("=")[1].trim();
+                        registryUrl = formUrl(registryAddress, group, username, password);
                     } else if (s.startsWith(Constants.METADATA_ADDRESS)) {
+                        String metadataAddress = s.split("=")[1].trim();
                         metadataUrl = formUrl(s.split("=")[1].trim(), group, username, password);
                     }
                 });
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ManagementController.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ManagementController.java
new file mode 100644
index 0000000..412a797
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/ManagementController.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+package org.apache.dubbo.admin.controller;
+
+import org.apache.dubbo.admin.common.exception.ParamValidationException;
+import org.apache.dubbo.admin.common.exception.ResourceNotFoundException;
+import org.apache.dubbo.admin.common.util.Constants;
+import org.apache.dubbo.admin.model.dto.ConfigDTO;
+import org.apache.dubbo.admin.service.ManagementService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+@RestController
+@RequestMapping("/api/{env}/manage")
+public class ManagementController {
+
+    private final ManagementService managementService;
+    private static Pattern CLASS_NAME_PATTERN = Pattern.compile("([a-zA-Z_$][a-zA-Z\\d_$]*\\.)*[a-zA-Z_$][a-zA-Z\\d_$]*");
+
+
+    @Autowired
+    public ManagementController(ManagementService managementService) {
+        this.managementService = managementService;
+    }
+
+    @RequestMapping(value ="/config", method = RequestMethod.POST)
+    @ResponseStatus(HttpStatus.CREATED)
+    public boolean createConfig(@RequestBody ConfigDTO config, @PathVariable String env) {
+        managementService.setConfig(config);
+        return true;
+    }
+
+    @RequestMapping(value = "/config/{key}", method = RequestMethod.PUT)
+    public boolean updateConfig(@PathVariable String key, @RequestBody ConfigDTO configDTO, @PathVariable String env) {
+        if (key == null) {
+            throw new ParamValidationException("Unknown ID!");
+        }
+        String exitConfig = managementService.getConfig(key);
+        if (exitConfig == null) {
+            throw new ResourceNotFoundException("Unknown ID!"); }
+        return managementService.updateConfig(configDTO);
+    }
+
+    @RequestMapping(value = "/config/{key}", method = RequestMethod.GET)
+    public List<ConfigDTO> getConfig(@PathVariable String key, @PathVariable String env) {
+        String config = managementService.getConfig(key);
+        List<ConfigDTO> configDTOs = new ArrayList<>();
+        if (config == null) {
+            return configDTOs;
+        }
+        ConfigDTO configDTO = new ConfigDTO();
+        configDTO.setKey(key);
+        configDTO.setConfig(config);
+        configDTO.setPath(managementService.getConfigPath(key));
+        if (Constants.GLOBAL_CONFIG.equals(key)) {
+            configDTO.setScope(Constants.GLOBAL_CONFIG);
+        } else if(CLASS_NAME_PATTERN.matcher(key).matches()){
+            configDTO.setScope(Constants.SERVICE);
+        } else {
+            configDTO.setScope(Constants.APPLICATION);
+        }
+        configDTOs.add(configDTO);
+        return configDTOs;
+    }
+
+    @RequestMapping(value = "/config/{key}", method = RequestMethod.DELETE)
+    public boolean deleteConfig(@PathVariable String key, @PathVariable String env) {
+        return managementService.deleteConfig(key);
+    }
+}
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/dto/ConfigDTO.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/dto/ConfigDTO.java
new file mode 100644
index 0000000..439fab8
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/model/dto/ConfigDTO.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package org.apache.dubbo.admin.model.dto;
+
+public class ConfigDTO {
+
+    private String key;
+    private String config;
+    private String scope;
+    private String path;
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getConfig() {
+        return config;
+    }
+
+    public void setConfig(String config) {
+        this.config = config;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public void setScope(String scope) {
+        this.scope = scope;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+}
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/GovernanceConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/GovernanceConfiguration.java
index c6ed397..4cae7c8 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/GovernanceConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/GovernanceConfiguration.java
@@ -19,6 +19,7 @@
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.SPI;
+import org.omg.PortableInterceptor.ServerRequestInfo;
 
 
 @SPI("zookeeper")
@@ -40,4 +41,8 @@
 
     boolean deleteConfig(String group, String key);
 
+    String getPath(String key);
+
+    String getPath(String group, String key);
+
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ApolloConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ApolloConfiguration.java
index e776f2c..58ce20c 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ApolloConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ApolloConfiguration.java
@@ -109,4 +109,14 @@
         client.removeItem(appId, env, cluster, group, key, "admin");
         return true;
     }
+
+    @Override
+    public String getPath(String key) {
+        return null;
+    }
+
+    @Override
+    public String getPath(String group, String key) {
+        return null;
+    }
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/NoOpConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/NoOpConfiguration.java
index 85dca2f..5c4fd18 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/NoOpConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/NoOpConfiguration.java
@@ -66,4 +66,14 @@
     public boolean deleteConfig(String group, String key) {
         return false;
     }
+
+    @Override
+    public String getPath(String key) {
+        return null;
+    }
+
+    @Override
+    public String getPath(String group, String key) {
+        return null;
+    }
 }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ZookeeperConfiguration.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ZookeeperConfiguration.java
index 0ba847a..a8d3138 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ZookeeperConfiguration.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/registry/config/impl/ZookeeperConfiguration.java
@@ -27,6 +27,8 @@
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.URL;
 
+import java.security.acl.Group;
+
 
 public class ZookeeperConfiguration implements GovernanceConfiguration {
     private static final Logger logger = LoggerFactory.getLogger(ZookeeperConfiguration.class);
@@ -119,6 +121,16 @@
         return true;
     }
 
+    @Override
+    public String getPath(String key) {
+        return getNodePath(key, null);
+    }
+
+    @Override
+    public String getPath(String group, String key) {
+        return getNodePath(key, group);
+    }
+
     private String getNodePath(String path, String group) {
         return toRootDir(group) + path;
     }
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/ManagementService.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/ManagementService.java
new file mode 100644
index 0000000..9d0a388
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/ManagementService.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package org.apache.dubbo.admin.service;
+
+import org.apache.dubbo.admin.model.dto.ConfigDTO;
+
+public interface ManagementService {
+
+
+    void setConfig(ConfigDTO config);
+
+    String getConfig(String key);
+
+    String getConfigPath(String key);
+
+    boolean updateConfig(ConfigDTO configDTO);
+
+    boolean deleteConfig(String key);
+}
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/ManagementServiceImpl.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/ManagementServiceImpl.java
new file mode 100644
index 0000000..d43e6de
--- /dev/null
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/service/impl/ManagementServiceImpl.java
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package org.apache.dubbo.admin.service.impl;
+
+import org.apache.dubbo.admin.common.util.Constants;
+import org.apache.dubbo.admin.model.dto.ConfigDTO;
+import org.apache.dubbo.admin.service.ManagementService;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ManagementServiceImpl extends AbstractService implements ManagementService {
+
+
+    private static String globalConfigPath = "config/dubbo/dubbo.properties";
+
+    @Override
+    public void setConfig(ConfigDTO config) {
+        if (Constants.GLOBAL_CONFIG.equals(config.getKey())) {
+            dynamicConfiguration.setConfig(globalConfigPath, config.getConfig());
+        } else {
+            dynamicConfiguration.setConfig(getPath(config.getKey()), config.getConfig());
+        }
+    }
+
+    @Override
+    public String getConfig(String key) {
+        if (Constants.GLOBAL_CONFIG.equals(key)) {
+            return dynamicConfiguration.getConfig(globalConfigPath);
+        }
+        return dynamicConfiguration.getConfig(getPath(key));
+    }
+
+    @Override
+    public String getConfigPath(String key) {
+        if (Constants.GLOBAL_CONFIG.equals(key)) {
+            return dynamicConfiguration.getPath(globalConfigPath);
+        }
+        return dynamicConfiguration.getPath(getPath(key));
+    }
+
+    @Override
+    public boolean updateConfig(ConfigDTO configDTO) {
+        String key = configDTO.getKey();
+        if (Constants.GLOBAL_CONFIG.equals(key)) {
+            dynamicConfiguration.setConfig(globalConfigPath, configDTO.getConfig());
+        } else {
+            dynamicConfiguration.setConfig(getPath(key), configDTO.getConfig());
+        }
+        return true;
+    }
+
+    @Override
+    public boolean deleteConfig(String key) {
+        if (Constants.GLOBAL_CONFIG.equals(key)) {
+            dynamicConfiguration.deleteConfig(globalConfigPath);
+        } else {
+            dynamicConfiguration.deleteConfig(getPath(key));
+        }
+        return true;
+    }
+
+    private String getPath(String key) {
+        return Constants.CONFIG_KEY + Constants.PATH_SEPARATOR + key + Constants.PATH_SEPARATOR
+                + Constants.DUBBO_PROPERTY;
+    }
+
+}
diff --git a/dubbo-admin-backend/src/main/resources/application.properties b/dubbo-admin-backend/src/main/resources/application.properties
index 429a7c0..c3b0ed5 100644
--- a/dubbo-admin-backend/src/main/resources/application.properties
+++ b/dubbo-admin-backend/src/main/resources/application.properties
@@ -15,8 +15,6 @@
 # limitations under the License.
 #
 
-admin.config-center.username=username
-admin.config-center.password=password
 admin.config-center=zookeeper://127.0.0.1:2181
 admin.registry.group=dubbo
 admin.registry.address=zookeeper://127.0.0.1:2181
diff --git a/dubbo-admin-frontend/src/api/menu.js b/dubbo-admin-frontend/src/api/menu.js
index 6f5ccac..05c058c 100644
--- a/dubbo-admin-frontend/src/api/menu.js
+++ b/dubbo-admin-frontend/src/api/menu.js
@@ -32,8 +32,8 @@
   },
   { title: 'serviceTest', path: '/test', icon: 'code', badge: 'feature' },
   { title: 'serviceMock', path: '/mock', icon: 'build', badge: 'feature' },
-  { title: 'metrics', path: '/metrics', icon: 'show_chart', badge: 'feature' }
-
+  { title: 'metrics', path: '/metrics', icon: 'show_chart', badge: 'feature' },
+  { title: 'configManage', path: '/management', icon: 'build', badge: 'feature' }
 ]
 
 export default Menu
diff --git a/dubbo-admin-frontend/src/components/Management.vue b/dubbo-admin-frontend/src/components/Management.vue
new file mode 100644
index 0000000..a04a3f2
--- /dev/null
+++ b/dubbo-admin-frontend/src/components/Management.vue
@@ -0,0 +1,293 @@
+<!--
+  - 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.
+  -->
+
+<template>
+  <v-container grid-list-xl fluid >
+    <v-layout row wrap>
+      <v-flex xs12 >
+        <search id="serviceSearch" v-model="filter" :submit="submit" :label="$t('searchDubboConfig')"></search>
+      </v-flex>
+    </v-layout>
+    <v-flex lg12>
+      <v-card>
+        <v-toolbar flat color="transparent" class="elevation-0">
+          <v-toolbar-title><span class="headline">{{$t('searchResult')}}</span></v-toolbar-title>
+          <v-spacer></v-spacer>
+          <v-btn outline color="primary" @click.stop="openDialog" class="mb-2">{{$t('create')}}</v-btn>
+        </v-toolbar>
+
+        <v-card-text class="pa-0" >
+          <v-data-table
+            :headers="headers"
+            :items="dubboConfig"
+            hide-actions
+            class="elevation-0"
+          >
+            <template slot="items" slot-scope="props">
+              <td class="text-xs-left">
+                <v-tooltip bottom >
+                   <span slot="activator">
+                     {{props.item.key}}
+                   </span>
+                  <span>{{props.item.path}}</span>
+                </v-tooltip>
+              </td>
+              <td class="text-xs-left">
+                <v-chip
+                  :color="getColor(props.item.scope)"
+                  text-color="white">
+                  {{props.item.scope}}
+                </v-chip>
+              </td>
+              <td class="text-xs-center px-0">
+                <v-tooltip bottom v-for="op in operations" :key="op.id">
+                  <v-icon small class="mr-2" slot="activator" @click="itemOperation(op.icon, props.item)">
+                    {{op.icon}}
+                  </v-icon>
+                  <span>{{$t(op.tooltip)}}</span>
+                </v-tooltip>
+              </td>
+            </template>
+          </v-data-table>
+        </v-card-text>
+      </v-card>
+    </v-flex>
+
+    <v-dialog   v-model="dialog" width="800px" persistent >
+      <v-card>
+        <v-card-title class="justify-center">
+          <span class="headline">{{$t('createNewDubboConfig')}}</span>
+        </v-card-title>
+        <v-card-text >
+          <v-text-field
+            :label="$t('appName')"
+            :hint="$t('configNameHint')"
+            v-model="key"
+          ></v-text-field>
+
+          <v-subheader class="pa-0 mt-3">{{$t('ruleContent')}}</v-subheader>
+          <ace-editor lang="properties" v-model="rule" :readonly="readonly"></ace-editor>
+
+        </v-card-text>
+        <v-card-actions>
+          <v-spacer></v-spacer>
+          <v-btn flat @click.native="closeDialog">{{$t('close')}}</v-btn>
+          <v-btn depressed color="primary" @click.native="saveItem">{{$t('save')}}</v-btn>
+        </v-card-actions>
+      </v-card>
+    </v-dialog>
+
+    <v-dialog v-model="warn.display" persistent max-width="500px">
+      <v-card>
+        <v-card-title class="headline">{{$t(this.warn.title) + this.warnStatus.id}}</v-card-title>
+        <v-card-text >{{this.warn.text}}</v-card-text>
+        <v-card-actions>
+          <v-spacer></v-spacer>
+          <v-btn flat @click.native="closeWarn">{{$t('cancel')}}</v-btn>
+          <v-btn depressed color="primary" @click.native="deleteItem(warnStatus)">{{$t('confirm')}}</v-btn>
+        </v-card-actions>
+      </v-card>
+    </v-dialog>
+
+  </v-container>
+</template>
+
+<script>
+    import AceEditor from '@/components/public/AceEditor'
+    import Search from '@/components/public/Search'
+    export default {
+      name: 'Management',
+      components: {
+        AceEditor,
+        Search
+      },
+      data: () => ({
+        configCenter: '',
+        rule: '',
+        updateId: '',
+        key: '',
+        filter: '',
+        readonly: false,
+        dialog: false,
+        operations: [
+          {id: 0, icon: 'visibility', tooltip: 'view'},
+          {id: 1, icon: 'edit', tooltip: 'edit'},
+          {id: 3, icon: 'delete', tooltip: 'delete'}
+        ],
+        warn: {
+          display: false,
+          title: '',
+          text: '',
+          status: {}
+        },
+        warnStatus: {},
+        dubboConfig: [],
+        headers: []
+      }),
+      methods: {
+        setHeaders () {
+          this.headers = [
+            {
+              text: this.$t('name'),
+              value: 'name',
+              align: 'left'
+            },
+            {
+              text: this.$t('scope'),
+              value: 'scope',
+              sortable: false
+            },
+            {
+              text: this.$t('operation'),
+              value: 'operation',
+              sortable: false,
+              width: '115px'
+            }
+          ]
+        },
+        itemOperation (icon, item) {
+          switch (icon) {
+            case 'visibility':
+              this.dialog = true
+              this.rule = item.config
+              this.key = item.key
+              this.readonly = true
+              this.updateId = 'close'
+              break
+            case 'edit':
+              this.dialog = true
+              this.rule = item.config
+              this.key = item.key
+              this.updateId = item.key
+              this.readonly = false
+              break
+            case 'delete':
+              this.openWarn('warnDeleteConfig')
+              this.warnStatus.id = item.key
+          }
+        },
+        deleteItem: function (warnStatus) {
+          this.$axios.delete('/manage/config/' + warnStatus.id)
+            .then(response => {
+              if (response.status === 200) {
+                this.warn.display = false
+                this.search(this.filter)
+                this.$notify.success('Delete success')
+              }
+            })
+        },
+        closeDialog: function () {
+          this.rule = ''
+          this.key = ''
+          this.dialog = false
+          this.readonly = false
+        },
+        openDialog: function () {
+          this.dialog = true
+        },
+        openWarn: function (title, text) {
+          this.warn.title = title
+          this.warn.text = text
+          this.warn.display = true
+        },
+        closeWarn: function () {
+          this.warn.title = ''
+          this.warn.text = ''
+          this.warn.display = false
+        },
+        saveItem: function () {
+          let configDTO = {}
+          if (!this.key) {
+            this.$notify.error('Config key is needed')
+            return
+          }
+          configDTO.key = this.key
+          configDTO.config = this.rule
+          let vm = this
+          if (this.updateId) {
+            if (this.updateId === 'close') {
+              this.closeDialog()
+            } else {
+              this.$axios.put('/manage/config/' + this.updateId, configDTO)
+                .then(response => {
+                  if (response.status === 200) {
+                    vm.search(vm.key)
+                    vm.filter = vm.key
+                    this.closeDialog()
+                    this.$notify.success('Update success')
+                  }
+                })
+            }
+          } else {
+            this.$axios.post('/manage/config/', configDTO)
+              .then(response => {
+                if (response.status === 201) {
+                  vm.search(vm.key)
+                  vm.filter = vm.key
+                  vm.closeDialog()
+                  vm.$notify.success('Create success')
+                }
+              })
+          }
+        },
+        getColor (scope) {
+          if (scope === 'global') {
+            return 'red'
+          }
+          if (scope === 'application') {
+            return 'green'
+          }
+          if (scope === 'service') {
+            return 'blue'
+          }
+        },
+        submit () {
+          this.filter = this.filter.trim()
+          this.search(this.filter)
+        },
+        search (filter) {
+          this.$axios.get('/manage/config/' + filter)
+            .then(response => {
+              if (response.status === 200) {
+                this.dubboConfig = response.data
+                this.$router.push({path: 'management', query: {key: filter}})
+              }
+            })
+        }
+      },
+      mounted () {
+        this.setHeaders()
+        let query = this.$route.query
+        let filter = null
+        Object.keys(query).forEach(function (key) {
+          if (key === 'key') {
+            filter = query[key]
+          }
+        })
+        if (filter !== null) {
+          this.filter = filter
+        } else {
+          this.filter = 'global'
+        }
+        this.search(this.filter)
+      }
+    }
+</script>
+
+<style scoped>
+
+</style>
diff --git a/dubbo-admin-frontend/src/components/governance/TagRule.vue b/dubbo-admin-frontend/src/components/governance/TagRule.vue
index e776576..dbda2f6 100644
--- a/dubbo-admin-frontend/src/components/governance/TagRule.vue
+++ b/dubbo-admin-frontend/src/components/governance/TagRule.vue
@@ -78,14 +78,14 @@
       </v-card>
     </v-dialog>
 
-    <v-dialog v-model="warn" persistent max-width="500px">
+    <v-dialog v-model="warn.display" persistent max-width="500px">
       <v-card>
-        <v-card-title class="headline">{{$t(this.warnTitle)}}</v-card-title>
-        <v-card-text >{{this.warnText}}</v-card-text>
+        <v-card-title class="headline">{{$t(this.warn.title)}}</v-card-title>
+        <v-card-text >{{this.warn.text}}</v-card-text>
         <v-card-actions>
           <v-spacer></v-spacer>
           <v-btn flat @click.native="closeWarn">CANCLE</v-btn>
-          <v-btn depressed color="primary" @click.native="deleteItem(warnStatus)">CONFIRM</v-btn>
+          <v-btn depressed color="primary" @click.native="deleteItem(warn.status)">{{$t('confirm')}}</v-btn>
         </v-card-actions>
       </v-card>
     </v-dialog>
@@ -110,12 +110,14 @@
       pattern: 'Service',
       filter: '',
       dialog: false,
-      warn: false,
       updateId: '',
       application: '',
-      warnTitle: '',
-      warnText: '',
-      warnStatus: {},
+      warn: {
+        display: false,
+        title: '',
+        text: '',
+        status: {}
+      },
       height: 0,
       operations: operations,
       tagRoutingRules: [
@@ -179,14 +181,14 @@
         this.dialog = true
       },
       openWarn: function (title, text) {
-        this.warnTitle = title
-        this.warnText = text
-        this.warn = true
+        this.warn.title = title
+        this.warn.text = text
+        this.warn.display = true
       },
       closeWarn: function () {
-        this.warnTitle = ''
-        this.warnText = ''
-        this.warn = false
+        this.warn.title = ''
+        this.warn.text = ''
+        this.warn.display = false
       },
       saveItem: function () {
         let rule = yaml.safeLoad(this.ruleText)
@@ -248,18 +250,18 @@
             break
           case 'block':
             this.openWarn(' Are you sure to block Tag Rule', 'application: ' + item.application)
-            this.warnStatus.operation = 'disable'
-            this.warnStatus.id = itemId
+            this.warn.status.operation = 'disable'
+            this.warn.status.id = itemId
             break
           case 'check_circle_outline':
             this.openWarn(' Are you sure to enable Tag Rule', 'application: ' + item.application)
-            this.warnStatus.operation = 'enable'
-            this.warnStatus.id = itemId
+            this.warn.status.operation = 'enable'
+            this.warn.status.id = itemId
             break
           case 'delete':
             this.openWarn(' Are you sure to Delete Tag Rule', 'application: ' + item.application)
-            this.warnStatus.operation = 'delete'
-            this.warnStatus.id = itemId
+            this.warn.status.operation = 'delete'
+            this.warn.status.id = itemId
         }
       },
       handleBalance: function (tagRoute, readonly) {
@@ -284,7 +286,7 @@
           this.$axios.delete('/rules/route/tag/' + id)
             .then(response => {
               if (response.status === 200) {
-                this.warn = false
+                this.warn.display = false
                 this.search(this.filter, false)
                 this.$notify.success('Delete success')
               }
@@ -293,7 +295,7 @@
           this.$axios.put('/rules/route/tag/disable/' + id)
             .then(response => {
               if (response.status === 200) {
-                this.warn = false
+                this.warn.display = false
                 this.search(this.filter, false)
                 this.$notify.success('Disable success')
               }
@@ -302,7 +304,7 @@
           this.$axios.put('/rules/route/tag/enable/' + id)
             .then(response => {
               if (response.status === 200) {
-                this.warn = false
+                this.warn.display = false
                 this.search(this.filter, false)
                 this.$notify.success('Enable success')
               }
diff --git a/dubbo-admin-frontend/src/lang/en.js b/dubbo-admin-frontend/src/lang/en.js
index 593206c..2b771d7 100644
--- a/dubbo-admin-frontend/src/lang/en.js
+++ b/dubbo-admin-frontend/src/lang/en.js
@@ -114,5 +114,13 @@
       rowsPerPageText: 'Rows per page:'
     },
     noDataText: 'No data available'
-  }
+  },
+  configManage: 'Configuration Management',
+  configCenterAddress: 'ConfigCenter Address',
+  searchDubboConfig: 'Search Dubbo Config',
+  createNewDubboConfig: 'Create New Dubbo Config',
+  scope: 'Scope',
+  name: 'Name',
+  warnDeleteConfig: ' Are you sure to Delete Dubbo Config: ',
+  configNameHint: "Application name the config belongs to, use 'global'(without quotes) for global config"
 }
diff --git a/dubbo-admin-frontend/src/lang/zh.js b/dubbo-admin-frontend/src/lang/zh.js
index fc28529..16517d8 100644
--- a/dubbo-admin-frontend/src/lang/zh.js
+++ b/dubbo-admin-frontend/src/lang/zh.js
@@ -114,5 +114,13 @@
       rowsPerPageText: '每页行数:'
     },
     noDataText: '无可用数据'
-  }
+  },
+  configManage: '配置管理',
+  configCenterAddress: '配置中心地址',
+  searchDubboConfig: '搜索Dubbo配置',
+  createNewDubboConfig: '新建Dubbo配置',
+  scope: '范围',
+  name: '名称',
+  warnDeleteConfig: ' 是否要删除Dubbo配置: ',
+  configNameHint: '配置所属的应用名, global 表示全局配置'
 }
diff --git a/dubbo-admin-frontend/src/router/index.js b/dubbo-admin-frontend/src/router/index.js
index d67bd0c..2ef4da4 100644
--- a/dubbo-admin-frontend/src/router/index.js
+++ b/dubbo-admin-frontend/src/router/index.js
@@ -28,6 +28,7 @@
 import ServiceTest from '@/components/test/ServiceTest'
 import ServiceMock from '@/components/test/ServiceMock'
 import ServiceMetrics from '@/components/metrics/ServiceMetrics'
+import Management from '@/components/Management'
 
 Vue.use(Router)
 
@@ -86,6 +87,11 @@
       path: '/metrics',
       name: 'ServiceMetrics',
       component: ServiceMetrics
+    },
+    {
+      path: '/management',
+      name: 'Management',
+      component: Management
     }
 
   ]