diff --git a/pom.xml b/pom.xml index 8837a45..777db59 100644 --- a/pom.xml +++ b/pom.xml @@ -1,194 +1,212 @@ - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.4.10 - - - com.xuxd - kafka-console-ui - 0.0.1-SNAPSHOT - kafka-console-ui - Kafka console manage ui - - 1.8 - UTF-8 - 1.8 - 1.8 - ${project.basedir}/ui - 1.11.0 - 1.8 - - - - org.scala-lang - scala-library - 2.13.6 - - - org.scala-lang - scala-compiler - 2.13.6 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.4.10 + + + com.xuxd + kafka-console-ui + 0.0.1-SNAPSHOT + kafka-console-ui + Kafka console manage ui + + 1.8 + UTF-8 + 1.8 + 1.8 + ${project.basedir}/ui + 1.11.0 + 1.8 + + + + org.scala-lang + scala-library + 2.13.6 + + + org.scala-lang + scala-compiler + 2.13.6 + - - org.springframework.boot - spring-boot-starter - + + org.springframework.boot + spring-boot-starter + - - org.springframework.boot - spring-boot-starter-web - + + org.springframework.boot + spring-boot-starter-web + - - org.springframework.boot - spring-boot-starter-test - test - + + org.springframework.boot + spring-boot-starter-test + test + - - org.apache.kafka - kafka-clients - 2.8.0 - + + org.apache.kafka + kafka-clients + 2.8.0 + - - - org.apache.commons - commons-lang3 - 3.12.0 - + + + org.apache.commons + commons-lang3 + 3.12.0 + - - com.google.guava - guava - 23.0 - + + com.google.guava + guava + 23.0 + - - org.projectlombok - lombok - 1.18.20 - provided - - + + org.projectlombok + lombok + 1.18.20 + provided + + - + + ${project.artifactId} - - - - org.springframework.boot - spring-boot-maven-plugin - + + + org.springframework.boot + spring-boot-maven-plugin + - - org.scala-tools - maven-scala-plugin - 2.15.2 - - - scala-compile-first - - compile - - - - **/*.scala - - - - - + + - - org.codehaus.mojo - build-helper-maven-plugin - 3.2.0 - - - add-source - generate-sources - - add-source - - - - src/main/java - src/main/scala - - - - - + + + dev + + true + + - - maven-compiler-plugin - - ${compiler.version} - ${compiler.version} - ${project.build.sourceEncoding} - - - - com.github.eirslett - frontend-maven-plugin - ${frontend-maven-plugin.version} - - ${ui.path} - v8.17.0 - http://npm.taobao.org/mirrors/node/ - - - - - - - - - - npm install - - npm - - - install --registry=https://registry.npmjs.org/ - - - - npm run build - - npm - - - run build - - - - - - maven-resources-plugin - 3.1.0 - - ${project.build.sourceEncoding} - ${project.build.directory} - - - ${basedir}/target/classes/public - ${ui.path}/dist - - - ${basedir}/target/classes - src/main/resources - - - - - - + + deploy + + + + + org.scala-tools + maven-scala-plugin + 2.15.2 + + + scala-compile-first + + compile + + + + **/*.scala + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.2.0 + + + add-source + generate-sources + + add-source + + + + src/main/java + src/main/scala + + + + + + + + maven-compiler-plugin + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + ${ui.path} + v8.17.0 + http://npm.taobao.org/mirrors/node/ + + + + + + + + + + npm install + + npm + + + install --registry=https://registry.npmjs.org/ + + + + npm run build + + npm + + + run build + + + + + + maven-resources-plugin + 3.1.0 + + ${project.build.sourceEncoding} + ${project.build.directory} + + + ${basedir}/target/classes/public + ${ui.path}/dist + + + ${basedir}/target/classes + src/main/resources + + + + + + + + diff --git a/src/main/java/com/xuxd/kafka/console/beans/AclEntry.java b/src/main/java/com/xuxd/kafka/console/beans/AclEntry.java index 4f8ef77..3d392d0 100644 --- a/src/main/java/com/xuxd/kafka/console/beans/AclEntry.java +++ b/src/main/java/com/xuxd/kafka/console/beans/AclEntry.java @@ -1,7 +1,6 @@ package com.xuxd.kafka.console.beans; import java.util.Objects; -import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.apache.kafka.common.acl.AccessControlEntry; import org.apache.kafka.common.acl.AccessControlEntryFilter; @@ -21,7 +20,6 @@ import org.apache.kafka.common.security.auth.KafkaPrincipal; * @author xuxd * @date 2021-08-28 20:17:27 **/ -@Data public class AclEntry { private String resourceType; @@ -100,4 +98,72 @@ public class AclEntry { entry.setPermissionType(this.permissionType); return entry; } + + public String getResourceType() { + return resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPatternType() { + return patternType; + } + + public void setPatternType(String patternType) { + this.patternType = patternType; + } + + public String getPrincipal() { + return principal; + } + + public void setPrincipal(String principal) { + this.principal = principal; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getOperation() { + return operation; + } + + public void setOperation(String operation) { + this.operation = operation; + } + + public String getPermissionType() { + return permissionType; + } + + public void setPermissionType(String permissionType) { + this.permissionType = permissionType; + } + + @Override public String toString() { + return "AclEntry{" + + "resourceType='" + resourceType + '\'' + + ", name='" + name + '\'' + + ", patternType='" + patternType + '\'' + + ", principal='" + principal + '\'' + + ", host='" + host + '\'' + + ", operation='" + operation + '\'' + + ", permissionType='" + permissionType + '\'' + + '}'; + } } diff --git a/src/main/java/com/xuxd/kafka/console/service/impl/AclServiceImpl.java b/src/main/java/com/xuxd/kafka/console/service/impl/AclServiceImpl.java index 83d6cb5..c6ebd80 100644 --- a/src/main/java/com/xuxd/kafka/console/service/impl/AclServiceImpl.java +++ b/src/main/java/com/xuxd/kafka/console/service/impl/AclServiceImpl.java @@ -6,6 +6,7 @@ import com.xuxd.kafka.console.beans.CounterMap; import com.xuxd.kafka.console.beans.ResponseData; import com.xuxd.kafka.console.service.AclService; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -13,6 +14,7 @@ import java.util.stream.Collectors; import kafka.console.KafkaAclConsole; import kafka.console.KafkaConfigConsole; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.apache.kafka.common.acl.AclBinding; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -35,7 +37,7 @@ public class AclServiceImpl implements AclService { @Override public ResponseData> getUserList() { try { - return ResponseData.create(Set.class).data(configConsole.getUserList()).success(); + return ResponseData.create(Set.class).data(configConsole.getUserList(null)).success(); } catch (Exception e) { log.error("getUserList error.", e); return ResponseData.create().failed(); @@ -60,8 +62,21 @@ public class AclServiceImpl implements AclService { List aclBindingList = entry.isNull() ? aclConsole.getAclList(null) : aclConsole.getAclList(entry); List entryList = aclBindingList.stream().map(x -> AclEntry.valueOf(x)).collect(Collectors.toList()); Map> entryMap = entryList.stream().collect(Collectors.groupingBy(AclEntry::getPrincipal)); + Map>> resultMap = new HashMap<>(); + entryMap.forEach((k, v) -> { + Map> map = v.stream().collect(Collectors.groupingBy(e -> e.getResourceType() + "#" + e.getName())); + resultMap.put(k, map); + }); + if (entry.isNull() || StringUtils.isNotBlank(entry.getPrincipal())) { + Set userList = configConsole.getUserList(StringUtils.isNotBlank(entry.getPrincipal()) ? Collections.singletonList(entry.getPrincipal()) : null); + userList.forEach(u -> { + if (!resultMap.containsKey(u)) { + resultMap.put(u, Collections.emptyMap()); + } + }); + } - return ResponseData.create().data(new CounterMap<>(entryMap)).success(); + return ResponseData.create().data(new CounterMap<>(resultMap)).success(); } @Override public ResponseData deleteAcl(AclEntry entry) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 03b586e..834e2f3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,5 +1,7 @@ server: port: 7766 + servlet: + context-path: /kafka-console kafka: config: diff --git a/src/main/scala/kafka/console/KafkaAclConsole.scala b/src/main/scala/kafka/console/KafkaAclConsole.scala index 8e7c0f1..590edf4 100644 --- a/src/main/scala/kafka/console/KafkaAclConsole.scala +++ b/src/main/scala/kafka/console/KafkaAclConsole.scala @@ -41,7 +41,7 @@ class KafkaAclConsole(config: KafkaConfig) extends KafkaConsole(config: KafkaCon } var principal: String = null - if ( StringUtils.isNotBlank(entry.getPrincipal) && !KafkaPrincipal.ANONYMOUS.toString.equalsIgnoreCase(f.entryFilter().principal())) { + if (StringUtils.isNotBlank(entry.getPrincipal()) && !KafkaPrincipal.ANONYMOUS.toString.equalsIgnoreCase(f.entryFilter().principal())) { principal = f.entryFilter().principal(); } val filter = new AclBindingFilter(new ResourcePatternFilter(resourceType, name, f.patternFilter().patternType()), diff --git a/src/main/scala/kafka/console/KafkaConfigConsole.scala b/src/main/scala/kafka/console/KafkaConfigConsole.scala index efe3ce9..6c4c8c1 100644 --- a/src/main/scala/kafka/console/KafkaConfigConsole.scala +++ b/src/main/scala/kafka/console/KafkaConfigConsole.scala @@ -17,9 +17,9 @@ class KafkaConfigConsole(config: KafkaConfig) extends KafkaConsole(config: Kafka private val defaultIterations = 4096 - def getUserList(): Set[String] = { + def getUserList(users: util.List[String]): Set[String] = { withAdminClient({ - adminClient => adminClient.describeUserScramCredentials().all().get().keySet() + adminClient => adminClient.describeUserScramCredentials(users).all().get().keySet() }).asInstanceOf[Set[String]] } diff --git a/ui/package-lock.json b/ui/package-lock.json index d1484a7..b9632f2 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -2587,6 +2587,14 @@ "integrity": "sha1-1h9G2DslGSUOJ4Ta9bCUeai0HFk=", "dev": true }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npm.taobao.org/babel-eslint/download/babel-eslint-10.1.0.tgz", @@ -5826,8 +5834,7 @@ "follow-redirects": { "version": "1.14.2", "resolved": "https://registry.nlark.com/follow-redirects/download/follow-redirects-1.14.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.14.2.tgz", - "integrity": "sha1-zsuCUEfAD15msUL5D+1PUV3seJs=", - "dev": true + "integrity": "sha1-zsuCUEfAD15msUL5D+1PUV3seJs=" }, "for-in": { "version": "1.0.2", diff --git a/ui/package.json b/ui/package.json index f46bbca..5408661 100644 --- a/ui/package.json +++ b/ui/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "ant-design-vue": "^1.7.8", + "axios": "^0.21.1", "core-js": "^3.6.5", "moment": "^2.29.1", "vue": "^2.6.11", diff --git a/ui/src/main.js b/ui/src/main.js index e85752c..3f9f7ef 100644 --- a/ui/src/main.js +++ b/ui/src/main.js @@ -5,9 +5,11 @@ import store from "./store"; // eslint-disable-next-line no-unused-vars import Antd from "ant-design-vue"; import "ant-design-vue/dist/antd.css"; +import { VueAxios } from "./utils/request"; Vue.config.productionTip = false; Vue.use(Antd); +Vue.use(VueAxios); new Vue({ router, diff --git a/ui/src/utils/axios.js b/ui/src/utils/axios.js new file mode 100644 index 0000000..c28a7ae --- /dev/null +++ b/ui/src/utils/axios.js @@ -0,0 +1,33 @@ +const VueAxios = { + vm: {}, + // eslint-disable-next-line no-unused-vars + install(Vue, instance) { + if (this.installed) { + return; + } + this.installed = true; + + if (!instance) { + // eslint-disable-next-line no-console + console.error("You have to install axios"); + return; + } + + Vue.axios = instance; + + Object.defineProperties(Vue.prototype, { + axios: { + get: function get() { + return instance; + }, + }, + $http: { + get: function get() { + return instance; + }, + }, + }); + }, +}; + +export { VueAxios }; diff --git a/ui/src/utils/request.js b/ui/src/utils/request.js new file mode 100644 index 0000000..67136df --- /dev/null +++ b/ui/src/utils/request.js @@ -0,0 +1,44 @@ +import axios from "axios"; +import notification from "ant-design-vue/es/notification"; +import { VueAxios } from "./axios"; + +// 创建 axios 实例 +const request = axios.create({ + // API 请求的默认前缀 + baseURL: "/kafka-console", + timeout: 10000, // 请求超时时间 +}); + +// 异常拦截处理器 +const errorHandler = (error) => { + if (error.response) { + const data = error.response.data; + notification.error({ + message: error.response.status, + description: JSON.stringify(data), + }); + } + return Promise.reject(error); +}; + +// request interceptor +// request.interceptors.request.use(config => { +// +// return config +// }, errorHandler) + +// response interceptor +request.interceptors.response.use((response) => { + return response.data; +}, errorHandler); + +const installer = { + vm: {}, + install(Vue) { + Vue.use(VueAxios, request); + }, +}; + +export default request; + +export { installer as VueAxios, request as axios }; diff --git a/ui/src/views/acl/Acl.vue b/ui/src/views/acl/Acl.vue index 660d76f..f87e911 100644 --- a/ui/src/views/acl/Acl.vue +++ b/ui/src/views/acl/Acl.vue @@ -1,6 +1,50 @@