diff --git a/CHANGES.md b/CHANGES.md index 5d536ab5fb9..2fb88508c9e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -32,6 +32,7 @@ Apollo 2.1.0 * [Add nodejs client sdk and fix doc](https://github.com/apolloconfig/apollo/pull/4590) * [Move apollo-core, apollo-client, apollo-mockserver, apollo-openapi and apollo-client-config-data to apollo-java repo](https://github.com/apolloconfig/apollo/pull/4594) * [fix get the openapi interface that contains namespace information for deleted items](https://github.com/apolloconfig/apollo/pull/4596) +* [A user-friendly config management page for apollo portal](https://github.com/apolloconfig/apollo/pull/4592) ------------------ All issues and pull requests are [here](https://github.com/apolloconfig/apollo/milestone/11?closed=1) \ No newline at end of file diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ServerConfigController.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ServerConfigController.java index 3623c14b831..0bf5bb94b8e 100644 --- a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ServerConfigController.java +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ServerConfigController.java @@ -20,7 +20,9 @@ import com.ctrip.framework.apollo.common.utils.BeanUtils; import com.ctrip.framework.apollo.portal.entity.po.ServerConfig; import com.ctrip.framework.apollo.portal.repository.ServerConfigRepository; +import com.ctrip.framework.apollo.portal.service.ServerConfigService; import com.ctrip.framework.apollo.portal.spi.UserInfoHolder; +import java.util.List; import java.util.Objects; import javax.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; @@ -38,10 +40,12 @@ public class ServerConfigController { private final ServerConfigRepository serverConfigRepository; private final UserInfoHolder userInfoHolder; + private final ServerConfigService serverConfigService; - public ServerConfigController(final ServerConfigRepository serverConfigRepository, final UserInfoHolder userInfoHolder) { + public ServerConfigController(final ServerConfigRepository serverConfigRepository, final UserInfoHolder userInfoHolder, final ServerConfigService serverConfigService) { this.serverConfigRepository = serverConfigRepository; this.userInfoHolder = userInfoHolder; + this.serverConfigService = serverConfigService; } @PreAuthorize(value = "@permissionValidator.isSuperAdmin()") @@ -63,6 +67,12 @@ public ServerConfig createOrUpdate(@Valid @RequestBody ServerConfig serverConfig return serverConfigRepository.save(storedConfig); } + @PreAuthorize(value = "@permissionValidator.isSuperAdmin()") + @GetMapping("/server/config/find-all-config") + public List findAllServerConfig() { + return serverConfigService.findAll(); + } + @PreAuthorize(value = "@permissionValidator.isSuperAdmin()") @GetMapping("/server/config/{key:.+}") public ServerConfig loadServerConfig(@PathVariable String key) { diff --git a/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ServerConfigService.java b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ServerConfigService.java new file mode 100644 index 00000000000..9cf5dabaf42 --- /dev/null +++ b/apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/service/ServerConfigService.java @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Apollo Authors + * + * Licensed 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 com.ctrip.framework.apollo.portal.service; + + +import com.ctrip.framework.apollo.portal.entity.po.ServerConfig; +import com.ctrip.framework.apollo.portal.repository.ServerConfigRepository; +import com.google.common.collect.Lists; +import java.util.List; +import org.springframework.stereotype.Service; + +@Service +public class ServerConfigService { + + private final ServerConfigRepository serverConfigRepository; + + public ServerConfigService(final ServerConfigRepository serverConfigRepository) { + this.serverConfigRepository = serverConfigRepository; + } + + public List findAll() { + Iterable serverConfigs = serverConfigRepository.findAll(); + return Lists.newArrayList(serverConfigs); + } +} diff --git a/apollo-portal/src/main/resources/static/config-manage.html b/apollo-portal/src/main/resources/static/config-manage.html new file mode 100644 index 00000000000..d4b3e00f84c --- /dev/null +++ b/apollo-portal/src/main/resources/static/config-manage.html @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + {{'ServiceConfig.Title' | translate }} + + + + + +
+
+
+
+
+ +
+
+
+
+
+ {{'ServiceConfig.Title' | translate }} + {{'ServiceConfig.Tips' | translate }} +
+ +
+
+ +
+ + +
+
+
+
+ + + + + + + + + + + + + + +
{{'Config.Key' | translate }}{{'Config.Value' | translate }}{{'Config.Comment' | translate }}{{'Config.Operation' | translate }}
{{ config.key }}{{ config.value }}{{ config.comment }} + + {{'UserMange.Edit' | translate }} + +
+ +
+
+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ + +
+
+
+
+
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ + +
+
+
+
+
+ +
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apollo-portal/src/main/resources/static/i18n/en.json b/apollo-portal/src/main/resources/static/i18n/en.json index d8bb15aaab9..64a88572181 100644 --- a/apollo-portal/src/main/resources/static/i18n/en.json +++ b/apollo-portal/src/main/resources/static/i18n/en.json @@ -432,6 +432,14 @@ "Config.DeleteNamespaceFailedTips": "The following projects are associated with this public namespace and they must all be deleted deleting the public Namespace", "Config.DeleteNamespaceNoPermissionFailedTitle": "Failed to delete", "Config.DeleteNamespaceNoPermissionFailedTips": "You do not have Project Administrator permission. Only Administrators can delete namespace. Please ask Project Administrators [{{users}}] to delete namespace.", + "Config.Key": "Key", + "Config.Value": "Value", + "Config.Comment": "Comment", + "Config.Operation": "Operation", + "Config.Add": "Add Config", + "Config.SortByKey": "Filter Config by Key", + "Config.FilterConfig": "Filter", + "Config.Reset": "Reset", "Delete.Title": "Delete applications, clusters, AppNamespace", "Delete.DeleteApp": "Delete application", "Delete.DeleteAppTips": "(Because deleting applications has very large impacts, only system administrators are allowed to delete them for the time being. Make sure that no client fetches the configuration of the application before deleting it.)", @@ -489,7 +497,7 @@ "Namespace.PleaseChooseCluster": "Select Cluster", "Namespace.CheckNamespaceNameLengthTip": "The namespace name should not be longer than 32 characters. Department prefix:'{{departmentLength}}' characters, name {{namespaceLength}} characters", "ServiceConfig.Title": "System Configuration", - "ServiceConfig.Tips": "(Maintain Apollo PortalDB.ServerConfig table data, will override configuration items if they already exist, or create configuration items. Configuration updates take effect automatically in a minute)", + "ServiceConfig.Tips": "(Maintain Apollo PortalDB.ServerConfig table data, will override configuration items if they already exist in the edit operation, or create configuration items. Configuration updates take effect automatically in a minute)", "ServiceConfig.Key": "Key", "ServiceConfig.KeyTips": "(Please query the configuration information before modifying the configuration)", "ServiceConfig.Value": "Value", diff --git a/apollo-portal/src/main/resources/static/i18n/zh-CN.json b/apollo-portal/src/main/resources/static/i18n/zh-CN.json index 859538a6de9..c50dfd07309 100644 --- a/apollo-portal/src/main/resources/static/i18n/zh-CN.json +++ b/apollo-portal/src/main/resources/static/i18n/zh-CN.json @@ -432,6 +432,14 @@ "Config.DeleteNamespaceFailedTips": "以下应用已关联此公共 Namespace,必须先删除全部已关联的 Namespace 才能删除公共 Namespace", "Config.DeleteNamespaceNoPermissionFailedTitle": "删除失败", "Config.DeleteNamespaceNoPermissionFailedTips": "您没有应用管理员权限,只有管理员才能删除 Namespace,请找应用管理员 [{{users}}] 删除 Namespace", + "Config.Key": "Key", + "Config.Value": "Value", + "Config.Comment": "Comment", + "Config.Operation": "Operation", + "Config.Add": "新增配置", + "Config.SortByKey": "按Key值过滤", + "Config.FilterConfig": "过滤配置", + "Config.Reset": "重置", "Delete.Title": "删除应用、集群、AppNamespace", "Delete.DeleteApp": "删除应用", "Delete.DeleteAppTips": "(由于删除应用影响面较大,所以现在暂时只允许系统管理员删除,请确保没有客户端读取该应用的配置后再做删除动作)", @@ -489,7 +497,7 @@ "Namespace.PleaseChooseCluster": "请选择集群", "Namespace.CheckNamespaceNameLengthTip": "Namespace 名称不能大于 32 个字符。 部门前缀:'{{departmentLength}}' 个字符, 名称 {{namespaceLength}} 个字符", "ServiceConfig.Title": "应用配置", - "ServiceConfig.Tips": "(维护 ApolloPortalDB.ServerConfig 表数据,如果已存在配置项则会覆盖,否则会创建配置项。配置更新后,一分钟后自动生效)", + "ServiceConfig.Tips": "(维护 ApolloPortalDB.ServerConfig 表数据,编辑操作中如果已存在配置项则会覆盖,否则会创建配置项。配置更新后,一分钟后自动生效)", "ServiceConfig.Key": "Key", "ServiceConfig.KeyTips": "(修改配置前请先查询该配置信息)", "ServiceConfig.Value": "Value", diff --git a/apollo-portal/src/main/resources/static/scripts/controller/ConfigController.js b/apollo-portal/src/main/resources/static/scripts/controller/ConfigController.js new file mode 100644 index 00000000000..43de8742411 --- /dev/null +++ b/apollo-portal/src/main/resources/static/scripts/controller/ConfigController.js @@ -0,0 +1,121 @@ +/* + * Copyright 2022 Apollo Authors + * + * Licensed 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. + * + */ +user_module.controller('ConfigController', + ['$scope', '$window', '$translate', 'toastr', 'AppUtil', 'ServerConfigService', 'PermissionService', + ConfigController]); + +function ConfigController($scope, $window, $translate, toastr, AppUtil, ServerConfigService, PermissionService) { + + $scope.serverConfig = {}; + $scope.createdConfigs = []; + $scope.filterConfig = []; + $scope.status = '1'; + $scope.searchKey = ''; + $scope.configEdit = configEdit; + $scope.create = create; + $scope.goback = goback; + $scope.portalDB = portalDB; + $scope.searchKeys = searchKeys; + $scope.resetSearchKey = resetSearchKey; + $scope.ConfigPage = 0; + + + initPermission(); + + getPortalDBConfig(); + + function initPermission() { + PermissionService.has_root_permission() + .then(function (result) { + $scope.isRootUser = result.hasPermission; + }) + } + + function getPortalDBConfig() { + ServerConfigService.findPortalDBConfig() + .then(function (result) { + if (!result || result.length === 0) { + $scope.ConfigPage = $scope.ConfigPage - 1; + return; + } + $scope.createdConfigs = []; + $scope.filterConfig = []; + result.forEach(function (user) { + $scope.createdConfigs.push(user); + $scope.filterConfig.push(user); + }); + }) + } + + + function configEdit (status,config) { + $scope.status = status; + + $scope.serverConfig = {}; + if (config != null) { + $scope.serverConfig = { + key: config.key, + value: config.value, + comment: config.comment + } + } + } + + function create() { + ServerConfigService.create($scope.serverConfig).then(function (result) { + toastr.success($translate.instant('ServiceConfig.Saved')); + $scope.serverConfig = result; + getPortalDBConfig(); + $scope.status = '1'; + }, function (result) { + toastr.error(AppUtil.errorMsg(result), $translate.instant('ServiceConfig.SaveFailed')); + }); + } + + + function goback(){ + $scope.status = '1'; + + getPortalDBConfig(); + } + + + function portalDB(){ + $scope.status = '1'; + $scope.ConfigPage = 0; + getPortalDBConfig(); + } + + function searchKeys() { + $scope.searchKey = $scope.searchKey.toLowerCase(); + var filterConfig = [] + $scope.createdConfigs.forEach(function (item) { + var keyName = item.key; + if (keyName && keyName.toLowerCase().indexOf( $scope.searchKey) >= 0) { + filterConfig.push(item); + } + }); + $scope.filterConfig = filterConfig + } + + function resetSearchKey() { + $scope.searchKey = '' + searchKeys() + } + + +} diff --git a/apollo-portal/src/main/resources/static/scripts/services/ServerConfigService.js b/apollo-portal/src/main/resources/static/scripts/services/ServerConfigService.js index 232d4577d99..b6aa0edd72c 100644 --- a/apollo-portal/src/main/resources/static/scripts/services/ServerConfigService.js +++ b/apollo-portal/src/main/resources/static/scripts/services/ServerConfigService.js @@ -23,6 +23,11 @@ appService.service('ServerConfigService', ['$resource', '$q', 'AppUtil', functio get_server_config_info: { method: 'GET', url: AppUtil.prefixPath() + '/server/config/:key' + }, + find_portal_db_config: { + method: 'GET', + isArray: true, + url: AppUtil.prefixPath() + '/server/config/find-all-config' } }); return { @@ -45,6 +50,16 @@ appService.service('ServerConfigService', ['$resource', '$q', 'AppUtil', functio d.reject(result); }); return d.promise; + }, + findPortalDBConfig:function (){ + var d = $q.defer(); + server_config_resource.find_portal_db_config({ + }, function (result) { + d.resolve(result); + }, function (result) { + d.reject(result); + }); + return d.promise; } } }]); diff --git a/apollo-portal/src/main/resources/static/views/common/nav.html b/apollo-portal/src/main/resources/static/views/common/nav.html index d0415f4dd5e..a887ace75e9 100644 --- a/apollo-portal/src/main/resources/static/views/common/nav.html +++ b/apollo-portal/src/main/resources/static/views/common/nav.html @@ -61,7 +61,7 @@
  • {{'Common.Nav.UserManage' | translate }}
  • {{'Common.Nav.SystemRoleManage' | translate }}
  • {{'Common.Nav.OpenMange' | translate }}
  • -
  • {{'Common.Nav.SystemConfig' | translate }}
  • +
  • {{'Common.Nav.SystemConfig' | translate }}
  • {{'Common.Nav.DeleteApp-Cluster-Namespace' | translate }}
  • {{'Common.Nav.SystemInfo' | translate }}
  • {{'Common.Nav.ConfigExport' | translate }}
  • diff --git a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ServerConfigControllerTest.java b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ServerConfigControllerTest.java index 3495d6d7b2b..6af63ca4d18 100644 --- a/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ServerConfigControllerTest.java +++ b/apollo-portal/src/test/java/com/ctrip/framework/apollo/portal/controller/ServerConfigControllerTest.java @@ -18,14 +18,22 @@ import com.ctrip.framework.apollo.portal.AbstractIntegrationTest; import com.ctrip.framework.apollo.portal.entity.po.ServerConfig; +import com.ctrip.framework.apollo.portal.service.ServerConfigService; import org.junit.Assert; import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.jdbc.Sql; import org.springframework.web.client.HttpClientErrorException; + +import java.util.*; + import static org.hamcrest.core.StringContains.containsString; import static org.junit.Assert.assertEquals; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.*; /** * Created by kezhenxu at 2019/1/14 13:24. @@ -34,7 +42,13 @@ */ @ActiveProfiles("skipAuthorization") public class ServerConfigControllerTest extends AbstractIntegrationTest { + @Mock + private ServerConfigService serverConfigService; + @InjectMocks + private ServerConfigController serverConfigController; + @Test + @Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) public void shouldSuccessWhenParameterValid() { ServerConfig serverConfig = new ServerConfig(); serverConfig.setKey("validKey"); @@ -76,4 +90,12 @@ public void shouldFailWhenParameterInvalid() { ); } } + + @Test + public void testFindEmpty() { + when(serverConfigService.findAll()).thenReturn(new ArrayList<>()); + List serverConfigList = serverConfigController.findAllServerConfig(); + Assert.assertNotNull(serverConfigList); + Assert.assertEquals(0, serverConfigList.size()); + } } diff --git a/doc/images/configure-view-permissions.png b/doc/images/configure-view-permissions.png new file mode 100644 index 00000000000..c5bc20d6ebc Binary files /dev/null and b/doc/images/configure-view-permissions.png differ diff --git a/doc/images/show-all-config.png b/doc/images/show-all-config.png new file mode 100644 index 00000000000..7a46607b65f Binary files /dev/null and b/doc/images/show-all-config.png differ diff --git a/docs/en/usage/apollo-user-guide.md b/docs/en/usage/apollo-user-guide.md index 55870600e03..d066a410246 100644 --- a/docs/en/usage/apollo-user-guide.md +++ b/docs/en/usage/apollo-user-guide.md @@ -458,7 +458,9 @@ The project members here are : The configuration is very simple. After logging in with your super administrator account, go to the `Administrator Tools - System Parameters` page and add or modify the `configView.memberOnly.envs` configuration item. -![configView.memberOnly.envs](https://user-images.githubusercontent.com/837658/46456519-c155e100-c7e1-11e8-969b-8f332379fa29.png) +![show-all-config](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/show-all-config.png) + +![configView.memberOnly.envs](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/configure-view-permissions.png) ## 6.2 Configuring access keys diff --git a/docs/zh/usage/apollo-user-guide.md b/docs/zh/usage/apollo-user-guide.md index 4d7f02215a3..4f6d17617b2 100644 --- a/docs/zh/usage/apollo-user-guide.md +++ b/docs/zh/usage/apollo-user-guide.md @@ -431,7 +431,9 @@ Apollo目前提供Java客户端,具体信息请点击[Java客户端使用文 配置方式很简单,用超级管理员账号登录后,进入`管理员工具 - 系统参数`页面新增或修改`configView.memberOnly.envs`配置项即可。 -![configView.memberOnly.envs](https://user-images.githubusercontent.com/837658/46456519-c155e100-c7e1-11e8-969b-8f332379fa29.png) +![show-all-config](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/show-all-config.png) + +![configView.memberOnly.envs](https://cdn.jsdelivr.net/gh/apolloconfig/apollo@master/doc/images/configure-view-permissions.png) ## 6.2 配置访问密钥