10 Commits

Author SHA1 Message Date
许晓东
ecbd9dda6a 开启集群数据权限,同一浏览器不同账号登录看到其它账号集群信息bug fixed. 2024-06-16 20:36:00 +08:00
许晓东
30707e84a7 brokerId不是从0开始,topic管理->变更副本失败fixed. 2024-06-11 20:00:23 +08:00
许晓东
f98cca8727 升级kafka到3.5.0版本. 2024-05-23 21:37:38 +08:00
许晓东
ea95697e88 长级kafka到3.5.0版本. 2024-05-23 21:35:34 +08:00
许晓东
e90268a56e issue #36, searchByTime maxNums 改为配置. 2024-03-03 21:46:45 +08:00
许晓东
fcc315782c 更新wechat. 2024-02-25 21:47:06 +08:00
许晓东
b79529f607 调整acl user用户名/密码长度. 2024-01-15 20:38:30 +08:00
许晓东
f49e2f7d0c 更新功能脑图 2024-01-06 20:48:50 +08:00
许晓东
4929953acd 登录按钮支持回车登录. 2023-12-23 21:14:38 +08:00
许晓东
8c946a96ba 发布1.0.9版本. 2023-12-06 20:41:37 +08:00
21 changed files with 118 additions and 15 deletions

View File

@@ -25,11 +25,11 @@ v1.0.6版本之前如果kafka集群启用了ACL但是控制台没看到Acl
![功能特性](./document/img/功能特性.png)
## 安装包下载
点击下载(v1.0.8版本)[kafka-console-ui.zip](https://github.com/xxd763795151/kafka-console-ui/releases/download/v1.0.8/kafka-console-ui.zip)
点击下载(v1.0.9版本)[kafka-console-ui.zip](https://github.com/xxd763795151/kafka-console-ui/releases/download/v1.0.9/kafka-console-ui-1.0.9.zip)
如果安装包下载的比较慢,可以查看下面的源码打包说明,把代码下载下来,本地快速打包.
github下载慢也可以试试从gitee下载点击下载[gitee来源kafka-console-ui.zip](https://gitee.com/xiaodong_xu/kafka-console-ui/releases/download/v1.0.8/kafka-console-ui.zip)
github下载慢也可以试试从gitee下载点击下载[gitee来源kafka-console-ui.zip](https://gitee.com/xiaodong_xu/kafka-console-ui/releases/download/v1.0.9/kafka-console-ui-1.0.9.zip)
## 快速使用
### Windows
@@ -99,6 +99,8 @@ auth:
## DockerCompose部署
感谢@wdkang123 同学分享的部署方式,如果有需要请查看[DockerCompose部署方式](./document/deploy/docker部署.md)
## 感谢支持
感谢jetbrains的开源支持如果有朋友愿意一起维护很欢迎提pr.
[![jetbrains](./document/img/jb_beam.svg "jetbrains")](https://jb.gg/OpenSourceSupport)
## 联系方式
+ 微信群

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 127 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 KiB

After

Width:  |  Height:  |  Size: 605 KiB

10
pom.xml
View File

@@ -10,7 +10,7 @@
</parent>
<groupId>com.xuxd</groupId>
<artifactId>kafka-console-ui</artifactId>
<version>1.0.9</version>
<version>1.1.0</version>
<name>kafka-console-ui</name>
<description>Kafka console manage ui</description>
<properties>
@@ -21,7 +21,7 @@
<ui.path>${project.basedir}/ui</ui.path>
<frontend-maven-plugin.version>1.11.0</frontend-maven-plugin.version>
<compiler.version>1.8</compiler.version>
<kafka.version>3.2.0</kafka.version>
<kafka.version>3.5.0</kafka.version>
<maven.assembly.plugin.version>3.0.0</maven.assembly.plugin.version>
<mybatis-plus-boot-starter.version>3.4.2</mybatis-plus-boot-starter.version>
<scala.version>2.13.6</scala.version>
@@ -90,6 +90,12 @@
</exclusions>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.apache.kafka</groupId>-->
<!-- <artifactId>kafka-tools</artifactId>-->
<!-- <version>${kafka.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.typesafe.scala-logging</groupId>
<artifactId>scala-logging_2.13</artifactId>

View File

@@ -33,4 +33,6 @@ public class QueryMessage {
private String headerKey;
private String headerValue;
private int filterNumber;
}

View File

@@ -37,6 +37,8 @@ public class QueryMessageDTO {
private String headerValue;
private int filterNumber;
public QueryMessage toQueryMessage() {
QueryMessage queryMessage = new QueryMessage();
queryMessage.setTopic(topic);
@@ -69,6 +71,7 @@ public class QueryMessageDTO {
if (StringUtils.isNotBlank(headerValue)) {
queryMessage.setHeaderValue(headerValue.trim());
}
queryMessage.setFilterNumber(filterNumber);
return queryMessage;
}

View File

@@ -33,4 +33,9 @@ public class AuthController {
public ResponseData login(@RequestBody LoginUserDTO userDTO) {
return authService.login(userDTO);
}
@GetMapping("/own/data/auth")
public boolean ownDataAuthority() {
return authService.ownDataAuthority();
}
}

View File

@@ -10,4 +10,6 @@ import com.xuxd.kafka.console.beans.dto.LoginUserDTO;
public interface AuthService {
ResponseData login(LoginUserDTO userDTO);
boolean ownDataAuthority();
}

View File

@@ -93,4 +93,16 @@ public class AuthServiceImpl implements AuthService {
return ResponseData.create().data(loginResult).success();
}
@Override
public boolean ownDataAuthority() {
if (!authConfig.isEnable()) {
return true;
}
if (!authConfig.isEnableClusterAuthority()) {
return true;
}
return false;
}
}

View File

@@ -70,7 +70,7 @@ public class MessageServiceImpl implements MessageService, ApplicationContextAwa
@Override
public ResponseData searchByTime(QueryMessage queryMessage) {
int maxNums = 5000;
int maxNums = queryMessage.getFilterNumber() <= 0 ? 5000 : queryMessage.getFilterNumber();
Object searchContent = null;
String headerKey = null;

View File

@@ -4,8 +4,8 @@
CREATE TABLE IF NOT EXISTS T_KAFKA_USER
(
ID IDENTITY NOT NULL COMMENT '主键ID',
USERNAME VARCHAR(50) NOT NULL DEFAULT '' COMMENT '用户名',
PASSWORD VARCHAR(50) NOT NULL DEFAULT '' COMMENT '密码',
USERNAME VARCHAR(128) NOT NULL DEFAULT '' COMMENT '用户名',
PASSWORD VARCHAR(128) NOT NULL DEFAULT '' COMMENT '密码',
UPDATE_TIME TIMESTAMP NOT NULL DEFAULT NOW() COMMENT '更新时间',
CLUSTER_INFO_ID BIGINT NOT NULL COMMENT '集群信息里的集群ID',
PRIMARY KEY (ID),

View File

@@ -21,7 +21,7 @@ import org.apache.kafka.common.utils.{KafkaThread, LogContext, Time}
import org.slf4j.{Logger, LoggerFactory}
import java.io.IOException
import java.util.Properties
import java.util.{Collections, Properties}
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.{ConcurrentLinkedQueue, TimeUnit}
import scala.jdk.CollectionConverters.{ListHasAsScala, MapHasAsJava, PropertiesHasAsScala, SetHasAsScala}
@@ -162,7 +162,7 @@ object BrokerApiVersion{
def listAllBrokerVersionInfo(): Map[Node, Try[NodeApiVersions]] =
findAllBrokers().map { broker =>
broker -> Try[NodeApiVersions](new NodeApiVersions(getApiVersions(broker)))
broker -> Try[NodeApiVersions](new NodeApiVersions(getApiVersions(broker), Collections.emptyList(), false))
}.toMap
def close(): Unit = {

View File

@@ -6,6 +6,7 @@ import {
setPermissions,
setToken,
setUsername,
deleteClusterInfo,
} from "@/utils/local-cache";
Vue.use(Vuex);
@@ -38,6 +39,9 @@ export default new Vuex.Store({
state.clusterInfo.enableSasl = enableSasl;
setClusterInfo(clusterInfo);
},
[CLUSTER.DELETE]() {
deleteClusterInfo();
},
[AUTH.ENABLE](state, enable) {
state.auth.enable = enable;
},

View File

@@ -1,5 +1,6 @@
export const CLUSTER = {
SWITCH: "switchCluster",
DELETE: "deleteClusterInfo",
};
export const AUTH = {

View File

@@ -365,6 +365,10 @@ export const AuthApi = {
url: "/auth/login",
method: "post",
},
ownDataAuthority: {
url: "/auth/own/data/auth",
method: "get",
},
};
export const ClusterRoleRelationApi = {

View File

@@ -4,6 +4,10 @@ export function setClusterInfo(clusterInfo) {
localStorage.setItem(Cache.clusterInfo, JSON.stringify(clusterInfo));
}
export function deleteClusterInfo() {
localStorage.removeItem(Cache.clusterInfo);
}
export function getClusterInfo() {
const str = localStorage.getItem(Cache.clusterInfo);
return str ? JSON.parse(str) : undefined;

View File

@@ -17,7 +17,7 @@
<a-input
placeholder="username"
:allowClear="true"
:maxLength="30"
:maxLength="100"
v-decorator="[
'username',
{
@@ -30,7 +30,7 @@
<a-input
placeholder="password"
:allowClear="true"
:maxLength="30"
:maxLength="100"
v-decorator="[
'password',
{

View File

@@ -9,6 +9,7 @@
<h3 class="login-title">登录kafka-console-ui</h3>
<a-form-item label="账号">
<a-input
@keyup.enter="handleSubmit"
style="width: 200px"
allowClear
v-decorator="[
@@ -20,6 +21,7 @@
</a-form-item>
<a-form-item label="密码">
<a-input-password
@keyup.enter="handleSubmit"
style="width: 200px"
v-decorator="[
'password',
@@ -28,7 +30,11 @@
/>
</a-form-item>
<a-form-item :wrapper-col="{ span: 16, offset: 5 }">
<a-button type="primary" @click="handleSubmit" :loading="loading"
<a-button
type="primary"
@click="handleSubmit"
:loading="loading"
@keyup.enter="handleSubmit"
>登录</a-button
>
</a-form-item>
@@ -40,7 +46,7 @@ import request from "@/utils/request";
import { AuthApi } from "@/utils/api";
import notification from "ant-design-vue/lib/notification";
import { mapMutations } from "vuex";
import { AUTH } from "@/store/mutation-types";
import { AUTH, CLUSTER } from "@/store/mutation-types";
export default {
name: "Login",
@@ -82,8 +88,19 @@ export default {
setToken: AUTH.SET_TOKEN,
setUsername: AUTH.SET_USERNAME,
setPermissions: AUTH.SET_PERMISSIONS,
deleteClusterInfo: CLUSTER.DELETE,
}),
},
created() {
request({
url: AuthApi.ownDataAuthority.url,
method: AuthApi.ownDataAuthority.method,
}).then((res) => {
if (!res) {
this.deleteClusterInfo();
}
});
},
};
</script>

View File

@@ -24,7 +24,11 @@
<DeleteMessage :topic-list="topicList"></DeleteMessage>
</a-tab-pane>
<a-tab-pane key="5" tab="发送统计" v-if="isAuthorized('message:send-statistics')">
<a-tab-pane
key="5"
tab="发送统计"
v-if="isAuthorized('message:send-statistics')"
>
<SendStatistics :topic-list="topicList"></SendStatistics>
</a-tab-pane>
</a-tabs>

View File

@@ -60,6 +60,33 @@
</a-col>
</a-row>
<hr class="hr" />
<a-row :gutter="24">
<a-col :span="24">
<a-form-item label="最大检索数">
<a-input-number
v-decorator="[
'filterNumber',
{
initialValue: 5000,
rules: [
{
required: true,
message: '输入消息数!',
},
],
},
]"
:min="1"
:max="100000"
/>
<span
>条
注意这里允许最多检索10万条但是不建议将该值设置过大这意味着一次查询要在内存里缓存这么多的数据可能导致内存溢出并且更大的消息量会导致更长的检索时间</span
>
</a-form-item>
</a-col>
</a-row>
<hr class="hr" />
<a-row :gutter="24">
<a-col :span="5">
<a-form-item label="消息过滤">

View File

@@ -91,6 +91,7 @@ export default {
loading: false,
form: this.$form.createForm(this, { name: "coordinated" }),
brokerSize: 0,
brokerIdList: [],
replicaNums: 0,
defaultReplicaNums: 0,
};
@@ -136,6 +137,8 @@ export default {
method: KafkaClusterApi.getClusterInfo.method,
}).then((res) => {
this.brokerSize = res.data.nodes.length;
this.brokerIdList = res.data.nodes.map((o) => o.id);
this.brokerIdList.sort((a, b) => a - b);
});
},
handleCancel() {
@@ -149,9 +152,16 @@ export default {
if (this.data.partitions.length > 0) {
this.data.partitions.forEach((p) => {
if (value > p.replicas.length) {
let min = this.brokerIdList[0];
let max = this.brokerIdList[this.brokerSize - 1] + 1;
let num = p.replicas[p.replicas.length - 1];
for (let i = p.replicas.length; i < value; i++) {
p.replicas.push(++num % this.brokerSize);
++num;
if (num < max) {
p.replicas.push(num);
} else {
p.replicas.push((num % max) + min);
}
}
}
if (value < p.replicas.length) {