合并主分支

This commit is contained in:
EricZeng
2022-09-22 17:49:04 +08:00
committed by GitHub
110 changed files with 2057 additions and 1747 deletions

View File

@@ -51,16 +51,16 @@
- 无需侵入改造 `Apache Kafka` ,一键便能纳管 `0.10.x` ~ `3.x.x` 众多版本的Kafka包括 `ZK``Raft` 运行模式的版本,同时在兼容架构上具备良好的扩展性,帮助您提升集群管理水平; - 无需侵入改造 `Apache Kafka` ,一键便能纳管 `0.10.x` ~ `3.x.x` 众多版本的Kafka包括 `ZK``Raft` 运行模式的版本,同时在兼容架构上具备良好的扩展性,帮助您提升集群管理水平;
- 🌪️  **零成本、界面化** - 🌪️  **零成本、界面化**
- 提炼高频 CLI 能力,设计合理的产品路径,提供清新美观的 GUI 界面,支持 Cluster、Broker、Topic、Group、Message、ACL 等组件 GUI 管理普通用户5分钟即可上手 - 提炼高频 CLI 能力,设计合理的产品路径,提供清新美观的 GUI 界面,支持 Cluster、Broker、Zookeeper、Topic、ConsumerGroup、Message、ACL、Connect 等组件 GUI 管理普通用户5分钟即可上手
- 👏  **云原生、插件化** - 👏  **云原生、插件化**
- 基于云原生构建,具备水平扩展能力,只需要增加节点即可获取更强的采集及对外服务能力,提供众多可热插拔的企业级特性,覆盖可观测性生态整合、资源治理、多活容灾等核心场景; - 基于云原生构建,具备水平扩展能力,只需要增加节点即可获取更强的采集及对外服务能力,提供众多可热插拔的企业级特性,覆盖可观测性生态整合、资源治理、多活容灾等核心场景;
- 🚀  **专业能力** - 🚀  **专业能力**
- 集群管理:支持集群一键纳管,健康分析、核心组件观测 等功能; - 集群管理:支持一键纳管,健康分析、核心组件观测 等功能;
- 观测提升:多维度指标观测大盘、观测指标最佳实践 等功能; - 观测提升:多维度指标观测大盘、观测指标最佳实践 等功能;
- 异常巡检:集群多维度健康巡检、集群多维度健康分 等功能; - 异常巡检:集群多维度健康巡检、集群多维度健康分 等功能;
- 能力增强Topic扩缩副本、Topic副本迁移 等功能; - 能力增强:集群负载均衡、Topic扩缩副本、Topic副本迁移 等功能;
   

View File

@@ -1,5 +1,39 @@
## v3.0.0-beta.3
**文档**
- FAQ 补充权限识别失败问题的说明
- 同步更新文档,保持与官网一致
**Bug修复**
- Offset 信息获取时,过滤掉无 Leader 的分区
- 升级 oshi-core 版本至 5.6.1 版本,修复 Windows 系统获取系统指标失败问题
- 修复 JMX 连接被关闭后,未进行重建的问题
- 修复因 DB 中 Broker 信息不存在导致 TotalLogSize 指标获取时抛空指针问题
- 修复 dml-logi.sql 中SQL 注释错误的问题
- 修复 startup.sh 中,识别操作系统类型错误的问题
- 修复配置管理页面删除配置失败的问题
- 修复系统管理应用文件引用路径
- 修复 Topic Messages 详情提示信息点击跳转 404 的问题
- 修复扩副本时,当前副本数不显示问题
**体验优化**
- Topic-Messages 页面增加返回数据的排序以及按照Earliest/Latest的获取方式
- 优化 GroupOffsetResetEnum 类名为 OffsetTypeEnum使得类名含义更准确
- 移动 KafkaZKDAO 类,及 Kafka Znode 实体类的位置,使得 Kafka Zookeeper DAO 更加内聚及便于识别
- 后端补充 Overview 页面指标排序的功能
- 前端 Webpack 配置优化
- Cluster Overview 图表取消放大展示功能
- 列表页增加手动刷新功能
- 接入/编辑集群,优化 JMX-PORTVersion 信息的回显优化JMX信息的展示
- 提高登录页面图片展示清晰度
- 部分样式和文案优化
---
## v3.0.0-beta.2 ## v3.0.0-beta.2
**文档** **文档**

View File

@@ -9,7 +9,7 @@ error_exit ()
[ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOME [ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOME
if [ -z "$JAVA_HOME" ]; then if [ -z "$JAVA_HOME" ]; then
if $darwin; then if [ "Darwin" = "$(uname -s)" ]; then
if [ -x '/usr/libexec/java_home' ] ; then if [ -x '/usr/libexec/java_home' ] ; then
export JAVA_HOME=`/usr/libexec/java_home` export JAVA_HOME=`/usr/libexec/java_home`

View File

@@ -59,6 +59,8 @@ sh deploy_KnowStreaming-offline.sh
### 2.1.3、容器部署 ### 2.1.3、容器部署
#### 2.1.3.1、Helm
**环境依赖** **环境依赖**
- Kubernetes >= 1.14 Helm >= 2.17.0 - Kubernetes >= 1.14 Helm >= 2.17.0
@@ -87,6 +89,103 @@ helm pull knowstreaming/knowstreaming-manager
   
#### 2.1.3.2、Docker Compose
```yml
version: "3"
services:
knowstreaming-manager:
image: knowstreaming/knowstreaming-manager:0.2.0-test
container_name: knowstreaming-manager
privileged: true
restart: always
depends_on:
- elasticsearch-single
- knowstreaming-mysql
expose:
- 80
command:
- /bin/sh
- /ks-start.sh
environment:
TZ: Asia/Shanghai
SERVER_MYSQL_ADDRESS: knowstreaming-mysql:3306
SERVER_MYSQL_DB: know_streaming
SERVER_MYSQL_USER: root
SERVER_MYSQL_PASSWORD: admin2022_
SERVER_ES_ADDRESS: elasticsearch-single:9200
JAVA_OPTS: -Xmx1g -Xms1g
# extra_hosts:
# - "hostname:x.x.x.x"
# volumes:
# - /ks/manage/log:/logs
knowstreaming-ui:
image: knowstreaming/knowstreaming-ui:0.2.0-test1
container_name: knowstreaming-ui
restart: always
ports:
- '18092:80'
environment:
TZ: Asia/Shanghai
depends_on:
- knowstreaming-manager
# extra_hosts:
# - "hostname:x.x.x.x"
elasticsearch-single:
image: docker.io/library/elasticsearch:7.6.2
container_name: elasticsearch-single
restart: always
expose:
- 9200
- 9300
# ports:
# - '9200:9200'
# - '9300:9300'
environment:
TZ: Asia/Shanghai
ES_JAVA_OPTS: -Xms512m -Xmx512m
discovery.type: single-node
# volumes:
# - /ks/es/data:/usr/share/elasticsearch/data
knowstreaming-init:
image: knowstreaming/knowstreaming-manager:0.2.0-test
container_name: knowstreaming_init
depends_on:
- elasticsearch-single
command:
- /bin/bash
- /es_template_create.sh
environment:
TZ: Asia/Shanghai
SERVER_ES_ADDRESS: elasticsearch-single:9200
knowstreaming-mysql:
image: knowstreaming/knowstreaming-mysql:0.2.0-test
container_name: knowstreaming-mysql
restart: always
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: admin2022_
MYSQL_DATABASE: know_streaming
MYSQL_ROOT_HOST: '%'
expose:
- 3306
# ports:
# - '3306:3306'
# volumes:
# - /ks/mysql/data:/data/mysql
```
 
### 2.1.4、手动部署 ### 2.1.4、手动部署
**部署流程** **部署流程**

View File

@@ -40,8 +40,7 @@ thread-pool:
``` ```
**SQL 变更**
**SQL变更**
```sql ```sql
-- 多集群管理权限2022-09-06新增 -- 多集群管理权限2022-09-06新增
@@ -80,12 +79,11 @@ ALTER TABLE `logi_security_oplog`
### 6.2.2、升级至 `v3.0.0-beta.1`版本 ### 6.2.2、升级至 `v3.0.0-beta.1`版本
**SQL 变更**
**SQL变更**
1、在`ks_km_broker`表增加了一个监听信息字段。 1、在`ks_km_broker`表增加了一个监听信息字段。
2、为`logi_security_oplog`表operation_methods字段设置默认值''。 2、为`logi_security_oplog` operation_methods 字段设置默认值''。
因此需要执行下面的sql对数据库表进行更新。 因此需要执行下面的 sql 对数据库表进行更新。
```sql ```sql
ALTER TABLE `ks_km_broker` ALTER TABLE `ks_km_broker`
@@ -98,7 +96,6 @@ ALTER COLUMN `operation_methods` set default '';
--- ---
### 6.2.3、`2.x`版本 升级至 `v3.0.0-beta.0`版本 ### 6.2.3、`2.x`版本 升级至 `v3.0.0-beta.0`版本
**升级步骤:** **升级步骤:**
@@ -123,14 +120,14 @@ ALTER COLUMN `operation_methods` set default '';
UPDATE ks_km_topic UPDATE ks_km_topic
INNER JOIN INNER JOIN
(SELECT (SELECT
topic.cluster_id AS cluster_id, topic.cluster_id AS cluster_id,
topic.topic_name AS topic_name, topic.topic_name AS topic_name,
topic.description AS description topic.description AS description
FROM topic WHERE description != '' FROM topic WHERE description != ''
) AS t ) AS t
ON ks_km_topic.cluster_phy_id = t.cluster_id ON ks_km_topic.cluster_phy_id = t.cluster_id
AND ks_km_topic.topic_name = t.topic_name AND ks_km_topic.topic_name = t.topic_name
AND ks_km_topic.id > 0 AND ks_km_topic.id > 0
SET ks_km_topic.description = t.description; SET ks_km_topic.description = t.description;
``` ```

View File

@@ -166,3 +166,19 @@ Node 版本: v12.22.12
需要到具体的应用中执行 `npm run start`,例如 `cd packages/layout-clusters-fe` 后,执行 `npm run start` 需要到具体的应用中执行 `npm run start`,例如 `cd packages/layout-clusters-fe` 后,执行 `npm run start`
应用启动后需要到基座应用中查看(需要启动基座应用,即 layout-clusters-fe 应用启动后需要到基座应用中查看(需要启动基座应用,即 layout-clusters-fe
## 8.12、权限识别失败问题
1、使用admin账号登陆KnowStreaming时点击系统管理-用户管理-角色管理-新增角色,查看页面是否正常。
<img src="http://img-ys011.didistatic.com/static/dc2img/do1_gwGfjN9N92UxzHU8dfzr" width = "400" >
2、查看'/logi-security/api/v1/permission/tree'接口返回值,出现如下图所示乱码现象。
![接口返回值](http://img-ys011.didistatic.com/static/dc2img/do1_jTxBkwNGU9vZuYQQbdNw)
3、查看logi_security_permission表看看是否出现了中文乱码现象。
根据以上几点,我们可以确定是由于数据库乱码造成的权限识别失败问题。
+ 原因:由于数据库编码和我们提供的脚本不一致,数据库里的数据发生了乱码,因此出现权限识别失败问题。
+ 解决方案清空数据库数据将数据库字符集调整为utf8最后重新执行[dml-logi.sql](https://github.com/didi/KnowStreaming/blob/master/km-dist/init/sql/dml-logi.sql)脚本导入数据即可。

View File

@@ -11,7 +11,7 @@
下面是用户第一次使用我们产品的典型体验路径: 下面是用户第一次使用我们产品的典型体验路径:
![text](http://img-ys011.didistatic.com/static/dc2img/do1_YehqxqmsVaqU5gf3XphI) ![text](http://img-ys011.didistatic.com/static/dc2img/do1_qgqPsAY46sZeBaPUCwXY)
## 5.3、常用功能 ## 5.3、常用功能

View File

@@ -19,7 +19,7 @@ import com.xiaojukeji.know.streaming.km.common.bean.vo.group.GroupTopicConsumedD
import com.xiaojukeji.know.streaming.km.common.bean.vo.group.GroupTopicOverviewVO; import com.xiaojukeji.know.streaming.km.common.bean.vo.group.GroupTopicOverviewVO;
import com.xiaojukeji.know.streaming.km.common.constant.MsgConstant; import com.xiaojukeji.know.streaming.km.common.constant.MsgConstant;
import com.xiaojukeji.know.streaming.km.common.enums.AggTypeEnum; import com.xiaojukeji.know.streaming.km.common.enums.AggTypeEnum;
import com.xiaojukeji.know.streaming.km.common.enums.GroupOffsetResetEnum; import com.xiaojukeji.know.streaming.km.common.enums.OffsetTypeEnum;
import com.xiaojukeji.know.streaming.km.common.enums.group.GroupStateEnum; import com.xiaojukeji.know.streaming.km.common.enums.group.GroupStateEnum;
import com.xiaojukeji.know.streaming.km.common.exception.AdminOperateException; import com.xiaojukeji.know.streaming.km.common.exception.AdminOperateException;
import com.xiaojukeji.know.streaming.km.common.exception.NotExistException; import com.xiaojukeji.know.streaming.km.common.exception.NotExistException;
@@ -199,12 +199,12 @@ public class GroupManagerImpl implements GroupManager {
return Result.buildFromRSAndMsg(ResultStatus.NOT_EXIST, MsgConstant.getTopicNotExist(dto.getClusterId(), dto.getTopicName())); return Result.buildFromRSAndMsg(ResultStatus.NOT_EXIST, MsgConstant.getTopicNotExist(dto.getClusterId(), dto.getTopicName()));
} }
if (GroupOffsetResetEnum.PRECISE_OFFSET.getResetType() == dto.getResetType() if (OffsetTypeEnum.PRECISE_OFFSET.getResetType() == dto.getResetType()
&& ValidateUtils.isEmptyList(dto.getOffsetList())) { && ValidateUtils.isEmptyList(dto.getOffsetList())) {
return Result.buildFromRSAndMsg(ResultStatus.PARAM_ILLEGAL, "参数错误指定offset重置需传offset信息"); return Result.buildFromRSAndMsg(ResultStatus.PARAM_ILLEGAL, "参数错误指定offset重置需传offset信息");
} }
if (GroupOffsetResetEnum.PRECISE_TIMESTAMP.getResetType() == dto.getResetType() if (OffsetTypeEnum.PRECISE_TIMESTAMP.getResetType() == dto.getResetType()
&& ValidateUtils.isNull(dto.getTimestamp())) { && ValidateUtils.isNull(dto.getTimestamp())) {
return Result.buildFromRSAndMsg(ResultStatus.PARAM_ILLEGAL, "参数错误,指定时间重置需传时间信息"); return Result.buildFromRSAndMsg(ResultStatus.PARAM_ILLEGAL, "参数错误,指定时间重置需传时间信息");
} }
@@ -213,7 +213,7 @@ public class GroupManagerImpl implements GroupManager {
} }
private Result<Map<TopicPartition, Long>> getPartitionOffset(GroupOffsetResetDTO dto) { private Result<Map<TopicPartition, Long>> getPartitionOffset(GroupOffsetResetDTO dto) {
if (GroupOffsetResetEnum.PRECISE_OFFSET.getResetType() == dto.getResetType()) { if (OffsetTypeEnum.PRECISE_OFFSET.getResetType() == dto.getResetType()) {
return Result.buildSuc(dto.getOffsetList().stream().collect(Collectors.toMap( return Result.buildSuc(dto.getOffsetList().stream().collect(Collectors.toMap(
elem -> new TopicPartition(dto.getTopicName(), elem.getPartitionId()), elem -> new TopicPartition(dto.getTopicName(), elem.getPartitionId()),
PartitionOffsetDTO::getOffset, PartitionOffsetDTO::getOffset,
@@ -222,9 +222,9 @@ public class GroupManagerImpl implements GroupManager {
} }
OffsetSpec offsetSpec = null; OffsetSpec offsetSpec = null;
if (GroupOffsetResetEnum.PRECISE_TIMESTAMP.getResetType() == dto.getResetType()) { if (OffsetTypeEnum.PRECISE_TIMESTAMP.getResetType() == dto.getResetType()) {
offsetSpec = OffsetSpec.forTimestamp(dto.getTimestamp()); offsetSpec = OffsetSpec.forTimestamp(dto.getTimestamp());
} else if (GroupOffsetResetEnum.EARLIEST.getResetType() == dto.getResetType()) { } else if (OffsetTypeEnum.EARLIEST.getResetType() == dto.getResetType()) {
offsetSpec = OffsetSpec.earliest(); offsetSpec = OffsetSpec.earliest();
} else { } else {
offsetSpec = OffsetSpec.latest(); offsetSpec = OffsetSpec.latest();

View File

@@ -1,5 +1,6 @@
package com.xiaojukeji.know.streaming.km.biz.topic; package com.xiaojukeji.know.streaming.km.biz.topic;
import com.xiaojukeji.know.streaming.km.common.bean.dto.pagination.PaginationSortDTO;
import com.xiaojukeji.know.streaming.km.common.bean.dto.topic.TopicRecordDTO; import com.xiaojukeji.know.streaming.km.common.bean.dto.topic.TopicRecordDTO;
import com.xiaojukeji.know.streaming.km.common.bean.entity.result.Result; import com.xiaojukeji.know.streaming.km.common.bean.entity.result.Result;
import com.xiaojukeji.know.streaming.km.common.bean.vo.topic.TopicBrokersPartitionsSummaryVO; import com.xiaojukeji.know.streaming.km.common.bean.vo.topic.TopicBrokersPartitionsSummaryVO;

View File

@@ -22,25 +22,26 @@ import com.xiaojukeji.know.streaming.km.common.bean.vo.topic.partition.TopicPart
import com.xiaojukeji.know.streaming.km.common.constant.Constant; import com.xiaojukeji.know.streaming.km.common.constant.Constant;
import com.xiaojukeji.know.streaming.km.common.constant.KafkaConstant; import com.xiaojukeji.know.streaming.km.common.constant.KafkaConstant;
import com.xiaojukeji.know.streaming.km.common.constant.MsgConstant; import com.xiaojukeji.know.streaming.km.common.constant.MsgConstant;
import com.xiaojukeji.know.streaming.km.common.converter.PartitionConverter;
import com.xiaojukeji.know.streaming.km.common.converter.TopicVOConverter; import com.xiaojukeji.know.streaming.km.common.converter.TopicVOConverter;
import com.xiaojukeji.know.streaming.km.common.enums.OffsetTypeEnum;
import com.xiaojukeji.know.streaming.km.common.enums.SortTypeEnum;
import com.xiaojukeji.know.streaming.km.common.exception.AdminOperateException; import com.xiaojukeji.know.streaming.km.common.exception.AdminOperateException;
import com.xiaojukeji.know.streaming.km.common.exception.NotExistException; import com.xiaojukeji.know.streaming.km.common.exception.NotExistException;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil; import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import com.xiaojukeji.know.streaming.km.common.utils.PaginationUtil;
import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils; import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils;
import com.xiaojukeji.know.streaming.km.core.service.broker.BrokerService; import com.xiaojukeji.know.streaming.km.core.service.broker.BrokerService;
import com.xiaojukeji.know.streaming.km.core.service.cluster.ClusterPhyService; import com.xiaojukeji.know.streaming.km.core.service.cluster.ClusterPhyService;
import com.xiaojukeji.know.streaming.km.core.service.partition.PartitionMetricService; import com.xiaojukeji.know.streaming.km.core.service.partition.PartitionMetricService;
import com.xiaojukeji.know.streaming.km.core.service.topic.TopicConfigService;
import com.xiaojukeji.know.streaming.km.core.service.partition.PartitionService; import com.xiaojukeji.know.streaming.km.core.service.partition.PartitionService;
import com.xiaojukeji.know.streaming.km.core.service.topic.TopicConfigService;
import com.xiaojukeji.know.streaming.km.core.service.topic.TopicMetricService; import com.xiaojukeji.know.streaming.km.core.service.topic.TopicMetricService;
import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService; import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService;
import com.xiaojukeji.know.streaming.km.core.service.version.metrics.TopicMetricVersionItems; import com.xiaojukeji.know.streaming.km.core.service.version.metrics.TopicMetricVersionItems;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.kafka.clients.admin.OffsetSpec; import org.apache.kafka.clients.admin.OffsetSpec;
import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.TopicPartition; import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.config.TopicConfig; import org.apache.kafka.common.config.TopicConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -160,8 +161,31 @@ public class TopicStateManagerImpl implements TopicStateManager {
} }
maxMessage = Math.min(maxMessage, dto.getMaxRecords()); maxMessage = Math.min(maxMessage, dto.getMaxRecords());
kafkaConsumer.assign(partitionList); kafkaConsumer.assign(partitionList);
Map<TopicPartition, OffsetAndTimestamp> partitionOffsetAndTimestampMap = new HashMap<>();
// 获取指定时间每个分区的offset按指定开始时间查询消息时
if (OffsetTypeEnum.PRECISE_TIMESTAMP.getResetType() == dto.getFilterOffsetReset()) {
Map<TopicPartition, Long> timestampsToSearch = new HashMap<>();
partitionList.forEach(topicPartition -> {
timestampsToSearch.put(topicPartition, dto.getStartTimestampUnitMs());
});
partitionOffsetAndTimestampMap = kafkaConsumer.offsetsForTimes(timestampsToSearch);
}
for (TopicPartition partition : partitionList) { for (TopicPartition partition : partitionList) {
kafkaConsumer.seek(partition, Math.max(beginOffsetsMapResult.getData().get(partition), endOffsetsMapResult.getData().get(partition) - dto.getMaxRecords())); if (OffsetTypeEnum.EARLIEST.getResetType() == dto.getFilterOffsetReset()) {
// 重置到最旧
kafkaConsumer.seek(partition, beginOffsetsMapResult.getData().get(partition));
} else if (OffsetTypeEnum.PRECISE_TIMESTAMP.getResetType() == dto.getFilterOffsetReset()) {
// 重置到指定时间
kafkaConsumer.seek(partition, partitionOffsetAndTimestampMap.get(partition).offset());
} else if (OffsetTypeEnum.PRECISE_OFFSET.getResetType() == dto.getFilterOffsetReset()) {
// 重置到指定位置
} else {
// 默认,重置到最新
kafkaConsumer.seek(partition, Math.max(beginOffsetsMapResult.getData().get(partition), endOffsetsMapResult.getData().get(partition) - dto.getMaxRecords()));
}
} }
// 这里需要减去 KafkaConstant.POLL_ONCE_TIMEOUT_UNIT_MS 是因为poll一次需要耗时如果这里不减去则可能会导致poll之后超过要求的时间 // 这里需要减去 KafkaConstant.POLL_ONCE_TIMEOUT_UNIT_MS 是因为poll一次需要耗时如果这里不减去则可能会导致poll之后超过要求的时间
@@ -185,6 +209,15 @@ public class TopicStateManagerImpl implements TopicStateManager {
} }
} }
// 排序
if (ObjectUtils.isNotEmpty(voList)) {
// 默认按时间倒序排序
if (StringUtils.isBlank(dto.getSortType())) {
dto.setSortType(SortTypeEnum.DESC.getSortType());
}
PaginationUtil.pageBySort(voList, dto.getSortField(), dto.getSortType());
}
return Result.buildSuc(voList.subList(0, Math.min(dto.getMaxRecords(), voList.size()))); return Result.buildSuc(voList.subList(0, Math.min(dto.getMaxRecords(), voList.size())));
} catch (Exception e) { } catch (Exception e) {
log.error("method=getTopicMessages||clusterPhyId={}||topicName={}||param={}||errMsg=exception", clusterPhyId, topicName, dto, e); log.error("method=getTopicMessages||clusterPhyId={}||topicName={}||param={}||errMsg=exception", clusterPhyId, topicName, dto, e);

View File

@@ -7,12 +7,14 @@ import com.didiglobal.logi.log.LogFactory;
import com.didiglobal.logi.security.common.dto.config.ConfigDTO; import com.didiglobal.logi.security.common.dto.config.ConfigDTO;
import com.didiglobal.logi.security.service.ConfigService; import com.didiglobal.logi.security.service.ConfigService;
import com.xiaojukeji.know.streaming.km.biz.version.VersionControlManager; import com.xiaojukeji.know.streaming.km.biz.version.VersionControlManager;
import com.xiaojukeji.know.streaming.km.common.bean.dto.metrices.MetricDetailDTO;
import com.xiaojukeji.know.streaming.km.common.bean.dto.metrices.UserMetricConfigDTO; import com.xiaojukeji.know.streaming.km.common.bean.dto.metrices.UserMetricConfigDTO;
import com.xiaojukeji.know.streaming.km.common.bean.entity.config.metric.UserMetricConfig; import com.xiaojukeji.know.streaming.km.common.bean.entity.config.metric.UserMetricConfig;
import com.xiaojukeji.know.streaming.km.common.bean.entity.result.Result; import com.xiaojukeji.know.streaming.km.common.bean.entity.result.Result;
import com.xiaojukeji.know.streaming.km.common.bean.entity.version.VersionControlItem; import com.xiaojukeji.know.streaming.km.common.bean.entity.version.VersionControlItem;
import com.xiaojukeji.know.streaming.km.common.bean.vo.config.metric.UserMetricConfigVO; import com.xiaojukeji.know.streaming.km.common.bean.vo.config.metric.UserMetricConfigVO;
import com.xiaojukeji.know.streaming.km.common.bean.vo.version.VersionItemVO; import com.xiaojukeji.know.streaming.km.common.bean.vo.version.VersionItemVO;
import com.xiaojukeji.know.streaming.km.common.constant.Constant;
import com.xiaojukeji.know.streaming.km.common.enums.version.VersionEnum; import com.xiaojukeji.know.streaming.km.common.enums.version.VersionEnum;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil; import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import com.xiaojukeji.know.streaming.km.common.utils.VersionUtil; import com.xiaojukeji.know.streaming.km.common.utils.VersionUtil;
@@ -47,29 +49,29 @@ public class VersionControlManagerImpl implements VersionControlManager {
@PostConstruct @PostConstruct
public void init(){ public void init(){
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_HEALTH_SCORE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_HEALTH_SCORE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_TOTAL_PRODUCE_REQUESTS, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_FAILED_FETCH_REQ, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_FAILED_FETCH_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_FAILED_PRODUCE_REQ, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_FAILED_PRODUCE_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_MESSAGE_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_UNDER_REPLICA_PARTITIONS, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_UNDER_REPLICA_PARTITIONS, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_TOTAL_PRODUCE_REQUESTS, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_IN, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_OUT, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_OUT, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_REJECTED, true)); defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_BYTES_REJECTED, true));
defaultMetrics.add(new UserMetricConfig(METRIC_TOPIC.getCode(), TOPIC_METRIC_MESSAGE_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_HEALTH_SCORE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_HEALTH_SCORE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_REQ_QUEUE_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_RES_QUEUE_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_ACTIVE_CONTROLLER_COUNT, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_ACTIVE_CONTROLLER_COUNT, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_PRODUCE_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_LOG_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_CONNECTIONS, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_MESSAGES_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_BYTES_IN, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_BYTES_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_BYTES_OUT, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_BYTES_OUT, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_GROUP_REBALANCES, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_CONNECTIONS, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_JOB_RUNNING, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_MESSAGES_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_PARTITIONS_NO_LEADER, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_PARTITIONS_NO_LEADER, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_PARTITION_URP, true)); defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_PARTITION_URP, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_LOG_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_PRODUCE_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_REQ_QUEUE_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_TOTAL_RES_QUEUE_SIZE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_GROUP_REBALANCES, true));
defaultMetrics.add(new UserMetricConfig(METRIC_CLUSTER.getCode(), CLUSTER_METRIC_JOB_RUNNING, true));
defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_OFFSET_CONSUMED, true)); defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_OFFSET_CONSUMED, true));
defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_LAG, true)); defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_LAG, true));
@@ -77,18 +79,18 @@ public class VersionControlManagerImpl implements VersionControlManager {
defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_HEALTH_SCORE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_GROUP.getCode(), GROUP_METRIC_HEALTH_SCORE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_HEALTH_SCORE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_HEALTH_SCORE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_REQ_QUEUE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_CONNECTION_COUNT, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_RES_QUEUE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_MESSAGE_IN, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_MESSAGE_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_PRODUCE_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_NETWORK_RPO_AVG_IDLE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_NETWORK_RPO_AVG_IDLE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_REQ_AVG_IDLE, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_REQ_AVG_IDLE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_CONNECTION_COUNT, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_PRODUCE_REQ, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_BYTES_IN, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_REQ_QUEUE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_BYTES_OUT, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_TOTAL_RES_QUEUE, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_PARTITIONS_SKEW, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_LEADERS_SKEW, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_LEADERS_SKEW, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_UNDER_REPLICATE_PARTITION, true)); defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_UNDER_REPLICATE_PARTITION, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_PARTITIONS_SKEW, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_BYTES_IN, true));
defaultMetrics.add(new UserMetricConfig(METRIC_BROKER.getCode(), BROKER_METRIC_BYTES_OUT, true));
} }
@Autowired @Autowired
@@ -159,6 +161,9 @@ public class VersionControlManagerImpl implements VersionControlManager {
UserMetricConfig umc = userMetricConfigMap.get(itemType + "@" + metric); UserMetricConfig umc = userMetricConfigMap.get(itemType + "@" + metric);
userMetricConfigVO.setSet(null != umc && umc.isSet()); userMetricConfigVO.setSet(null != umc && umc.isSet());
if (umc != null) {
userMetricConfigVO.setRank(umc.getRank());
}
userMetricConfigVO.setName(itemVO.getName()); userMetricConfigVO.setName(itemVO.getName());
userMetricConfigVO.setType(itemVO.getType()); userMetricConfigVO.setType(itemVO.getType());
userMetricConfigVO.setDesc(itemVO.getDesc()); userMetricConfigVO.setDesc(itemVO.getDesc());
@@ -178,13 +183,29 @@ public class VersionControlManagerImpl implements VersionControlManager {
@Override @Override
public Result<Void> updateUserMetricItem(Long clusterId, Integer type, UserMetricConfigDTO dto, String operator) { public Result<Void> updateUserMetricItem(Long clusterId, Integer type, UserMetricConfigDTO dto, String operator) {
Map<String, Boolean> metricsSetMap = dto.getMetricsSet(); Map<String, Boolean> metricsSetMap = dto.getMetricsSet();
if(null == metricsSetMap || metricsSetMap.isEmpty()){
//转换metricDetailDTOList
List<MetricDetailDTO> metricDetailDTOList = dto.getMetricDetailDTOList();
Map<String, MetricDetailDTO> metricDetailMap = new HashMap<>();
if (metricDetailDTOList != null && !metricDetailDTOList.isEmpty()) {
metricDetailMap = metricDetailDTOList.stream().collect(Collectors.toMap(MetricDetailDTO::getMetric, Function.identity()));
}
//转换metricsSetMap
if (metricsSetMap != null && !metricsSetMap.isEmpty()) {
for (Map.Entry<String, Boolean> metricAndShowEntry : metricsSetMap.entrySet()) {
if (metricDetailMap.containsKey(metricAndShowEntry.getKey())) continue;
metricDetailMap.put(metricAndShowEntry.getKey(), new MetricDetailDTO(metricAndShowEntry.getKey(), metricAndShowEntry.getValue(), null));
}
}
if (metricDetailMap.isEmpty()) {
return Result.buildSuc(); return Result.buildSuc();
} }
Set<UserMetricConfig> userMetricConfigs = getUserMetricConfig(operator); Set<UserMetricConfig> userMetricConfigs = getUserMetricConfig(operator);
for(Map.Entry<String, Boolean> metricAndShowEntry : metricsSetMap.entrySet()){ for (MetricDetailDTO metricDetailDTO : metricDetailMap.values()) {
UserMetricConfig userMetricConfig = new UserMetricConfig(type, metricAndShowEntry.getKey(), metricAndShowEntry.getValue()); UserMetricConfig userMetricConfig = new UserMetricConfig(type, metricDetailDTO.getMetric(), metricDetailDTO.getSet(), metricDetailDTO.getRank());
userMetricConfigs.remove(userMetricConfig); userMetricConfigs.remove(userMetricConfig);
userMetricConfigs.add(userMetricConfig); userMetricConfigs.add(userMetricConfig);
} }
@@ -228,7 +249,7 @@ public class VersionControlManagerImpl implements VersionControlManager {
return defaultMetrics; return defaultMetrics;
} }
return JSON.parseObject(value, new TypeReference<Set<UserMetricConfig>>(){}); return JSON.parseObject(value, new TypeReference<Set<UserMetricConfig>>() {});
} }
public static void main(String[] args){ public static void main(String[] args){

View File

@@ -3,6 +3,7 @@ package com.xiaojukeji.know.streaming.km.common.bean.dto.group;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.xiaojukeji.know.streaming.km.common.bean.dto.partition.PartitionOffsetDTO; import com.xiaojukeji.know.streaming.km.common.bean.dto.partition.PartitionOffsetDTO;
import com.xiaojukeji.know.streaming.km.common.bean.dto.topic.ClusterTopicDTO; import com.xiaojukeji.know.streaming.km.common.bean.dto.topic.ClusterTopicDTO;
import com.xiaojukeji.know.streaming.km.common.enums.OffsetTypeEnum;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@@ -23,7 +24,7 @@ public class GroupOffsetResetDTO extends ClusterTopicDTO {
private String groupName; private String groupName;
/** /**
* @see com.xiaojukeji.know.streaming.km.common.enums.GroupOffsetResetEnum * @see OffsetTypeEnum
*/ */
@NotNull(message = "resetType不允许为空") @NotNull(message = "resetType不允许为空")
@ApiModelProperty(value = "重置方式", example = "1") @ApiModelProperty(value = "重置方式", example = "1")

View File

@@ -0,0 +1,28 @@
package com.xiaojukeji.know.streaming.km.common.bean.dto.metrices;
import com.xiaojukeji.know.streaming.km.common.bean.dto.BaseDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author didi
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "指标详细属性信息")
public class MetricDetailDTO extends BaseDTO {
@ApiModelProperty("指标名称")
private String metric;
@ApiModelProperty("指标是否显示")
private Boolean set;
@ApiModelProperty("指标优先级")
private Integer rank;
}

View File

@@ -7,6 +7,7 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map; import java.util.Map;
@@ -17,4 +18,7 @@ import java.util.Map;
public class UserMetricConfigDTO extends BaseDTO { public class UserMetricConfigDTO extends BaseDTO {
@ApiModelProperty("指标展示设置项key指标名value是否展现(true展现/false不展现)") @ApiModelProperty("指标展示设置项key指标名value是否展现(true展现/false不展现)")
private Map<String, Boolean> metricsSet; private Map<String, Boolean> metricsSet;
@ApiModelProperty("指标自定义属性列表")
private List<MetricDetailDTO> metricDetailDTOList;
} }

View File

@@ -1,7 +1,8 @@
package com.xiaojukeji.know.streaming.km.common.bean.dto.topic; package com.xiaojukeji.know.streaming.km.common.bean.dto.topic;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.xiaojukeji.know.streaming.km.common.bean.dto.BaseDTO; import com.xiaojukeji.know.streaming.km.common.bean.dto.pagination.PaginationSortDTO;
import com.xiaojukeji.know.streaming.km.common.enums.OffsetTypeEnum;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@@ -15,7 +16,7 @@ import javax.validation.constraints.NotNull;
@Data @Data
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@ApiModel(description = "Topic记录") @ApiModel(description = "Topic记录")
public class TopicRecordDTO extends BaseDTO { public class TopicRecordDTO extends PaginationSortDTO {
@NotNull(message = "truncate不允许为空") @NotNull(message = "truncate不允许为空")
@ApiModelProperty(value = "是否截断", example = "true") @ApiModelProperty(value = "是否截断", example = "true")
private Boolean truncate; private Boolean truncate;
@@ -34,4 +35,13 @@ public class TopicRecordDTO extends BaseDTO {
@ApiModelProperty(value = "预览超时时间", example = "10000") @ApiModelProperty(value = "预览超时时间", example = "10000")
private Long pullTimeoutUnitMs = 8000L; private Long pullTimeoutUnitMs = 8000L;
/**
* @see OffsetTypeEnum
*/
@ApiModelProperty(value = "offset", example = "")
private Integer filterOffsetReset = 0;
@ApiModelProperty(value = "开始日期时间戳", example = "")
private Long startTimestampUnitMs;
} }

View File

@@ -5,7 +5,6 @@ import com.alibaba.fastjson.TypeReference;
import com.xiaojukeji.know.streaming.km.common.bean.entity.common.IpPortData; import com.xiaojukeji.know.streaming.km.common.bean.entity.common.IpPortData;
import com.xiaojukeji.know.streaming.km.common.bean.po.broker.BrokerPO; import com.xiaojukeji.know.streaming.km.common.bean.po.broker.BrokerPO;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil; import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import com.xiaojukeji.know.streaming.km.common.zookeeper.znode.brokers.BrokerMetadata;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -79,20 +78,6 @@ public class Broker implements Serializable {
return metadata; return metadata;
} }
public static Broker buildFrom(Long clusterPhyId, Integer brokerId, BrokerMetadata brokerMetadata) {
Broker metadata = new Broker();
metadata.setClusterPhyId(clusterPhyId);
metadata.setBrokerId(brokerId);
metadata.setHost(brokerMetadata.getHost());
metadata.setPort(brokerMetadata.getPort());
metadata.setJmxPort(brokerMetadata.getJmxPort());
metadata.setStartTimestamp(brokerMetadata.getTimestamp());
metadata.setRack(brokerMetadata.getRack());
metadata.setStatus(1);
metadata.setEndpointMap(brokerMetadata.getEndpointMap());
return metadata;
}
public static Broker buildFrom(BrokerPO brokerPO) { public static Broker buildFrom(BrokerPO brokerPO) {
Broker broker = ConvertUtil.obj2Obj(brokerPO, Broker.class); Broker broker = ConvertUtil.obj2Obj(brokerPO, Broker.class);
String endpointMapStr = brokerPO.getEndpointMap(); String endpointMapStr = brokerPO.getEndpointMap();

View File

@@ -1,12 +1,12 @@
package com.xiaojukeji.know.streaming.km.common.bean.entity.config.metric; package com.xiaojukeji.know.streaming.km.common.bean.entity.config.metric;
import com.xiaojukeji.know.streaming.km.common.constant.Constant;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor
public class UserMetricConfig { public class UserMetricConfig {
private int type; private int type;
@@ -15,6 +15,22 @@ public class UserMetricConfig {
private boolean set; private boolean set;
private Integer rank;
public UserMetricConfig(int type, String metric, boolean set, Integer rank) {
this.type = type;
this.metric = metric;
this.set = set;
this.rank = rank;
}
public UserMetricConfig(int type, String metric, boolean set) {
this.type = type;
this.metric = metric;
this.set = set;
this.rank = null;
}
@Override @Override
public int hashCode(){ public int hashCode(){
return metric.hashCode() << 1 + type; return metric.hashCode() << 1 + type;

View File

@@ -14,4 +14,7 @@ import lombok.NoArgsConstructor;
public class UserMetricConfigVO extends VersionItemVO { public class UserMetricConfigVO extends VersionItemVO {
@ApiModelProperty(value = "该指标用户是否设置展现", example = "true") @ApiModelProperty(value = "该指标用户是否设置展现", example = "true")
private Boolean set; private Boolean set;
@ApiModelProperty(value = "该指标展示优先级", example = "1")
private Integer rank;
} }

View File

@@ -42,6 +42,7 @@ public class Constant {
*/ */
public static final Integer DEFAULT_CLUSTER_HEALTH_SCORE = 90; public static final Integer DEFAULT_CLUSTER_HEALTH_SCORE = 90;
public static final String DEFAULT_USER_NAME = "know-streaming-app"; public static final String DEFAULT_USER_NAME = "know-streaming-app";
public static final int INVALID_CODE = -1; public static final int INVALID_CODE = -1;
@@ -64,4 +65,5 @@ public class Constant {
public static final Float COLLECT_METRICS_ERROR_COST_TIME = -1.0F; public static final Float COLLECT_METRICS_ERROR_COST_TIME = -1.0F;
public static final Integer DEFAULT_RETRY_TIME = 3; public static final Integer DEFAULT_RETRY_TIME = 3;
} }

View File

@@ -52,6 +52,10 @@ public class MsgConstant {
/**************************************************** Partition ****************************************************/ /**************************************************** Partition ****************************************************/
public static String getPartitionNoLeader(Long clusterPhyId, String topicName) {
return String.format("集群ID:[%d] Topic名称:[%s] 所有分区NoLeader", clusterPhyId, topicName);
}
public static String getPartitionNotExist(Long clusterPhyId, String topicName) { public static String getPartitionNotExist(Long clusterPhyId, String topicName) {
return String.format("集群ID:[%d] Topic名称:[%s] 存在非法的分区ID", clusterPhyId, topicName); return String.format("集群ID:[%d] Topic名称:[%s] 存在非法的分区ID", clusterPhyId, topicName);
} }

View File

@@ -3,19 +3,19 @@ package com.xiaojukeji.know.streaming.km.common.enums;
import lombok.Getter; import lombok.Getter;
/** /**
* 重置offset * offset类型
* @author zengqiao * @author zengqiao
* @date 19/4/8 * @date 19/4/8
*/ */
@Getter @Getter
public enum GroupOffsetResetEnum { public enum OffsetTypeEnum {
LATEST(0, "重置到最新"), LATEST(0, "最新"),
EARLIEST(1, "重置到最旧"), EARLIEST(1, "最旧"),
PRECISE_TIMESTAMP(2, "按时间进行重置"), PRECISE_TIMESTAMP(2, "指定时间"),
PRECISE_OFFSET(3, "重置到指定位置"), PRECISE_OFFSET(3, "指定位置"),
; ;
@@ -23,7 +23,7 @@ public enum GroupOffsetResetEnum {
private final String message; private final String message;
GroupOffsetResetEnum(int resetType, String message) { OffsetTypeEnum(int resetType, String message) {
this.resetType = resetType; this.resetType = resetType;
this.message = message; this.message = message;
} }

View File

@@ -90,6 +90,8 @@ public class JmxConnectorWrap {
} }
try { try {
jmxConnector.close(); jmxConnector.close();
jmxConnector = null;
} catch (IOException e) { } catch (IOException e) {
LOGGER.warn("close JmxConnector exception, physicalClusterId:{} brokerId:{} host:{} port:{}.", physicalClusterId, brokerId, host, port, e); LOGGER.warn("close JmxConnector exception, physicalClusterId:{} brokerId:{} host:{} port:{}.", physicalClusterId, brokerId, host, port, e);
} }
@@ -105,6 +107,11 @@ public class JmxConnectorWrap {
acquire(); acquire();
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection(); MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
return mBeanServerConnection.getAttribute(name, attribute); return mBeanServerConnection.getAttribute(name, attribute);
} catch (IOException ioe) {
// 如果是因为连接断开,则进行重新连接,并抛出异常
reInitDueIOException();
throw ioe;
} finally { } finally {
atomicInteger.incrementAndGet(); atomicInteger.incrementAndGet();
} }
@@ -120,6 +127,11 @@ public class JmxConnectorWrap {
acquire(); acquire();
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection(); MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
return mBeanServerConnection.getAttributes(name, attributes); return mBeanServerConnection.getAttributes(name, attributes);
} catch (IOException ioe) {
// 如果是因为连接断开,则进行重新连接,并抛出异常
reInitDueIOException();
throw ioe;
} finally { } finally {
atomicInteger.incrementAndGet(); atomicInteger.incrementAndGet();
} }
@@ -131,6 +143,11 @@ public class JmxConnectorWrap {
acquire(); acquire();
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection(); MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
return mBeanServerConnection.queryNames(name, query); return mBeanServerConnection.queryNames(name, query);
} catch (IOException ioe) {
// 如果是因为连接断开,则进行重新连接,并抛出异常
reInitDueIOException();
throw ioe;
} finally { } finally {
atomicInteger.incrementAndGet(); atomicInteger.incrementAndGet();
} }
@@ -186,4 +203,26 @@ public class JmxConnectorWrap {
} }
} }
} }
private synchronized void reInitDueIOException() {
try {
if (jmxConnector == null) {
return;
}
// 检查是否正常
jmxConnector.getConnectionId();
// 如果正常则直接返回
return;
} catch (Exception e) {
// ignore
}
// 关闭旧的
this.close();
// 重新创建
this.checkJmxConnectionAndInitIfNeed();
}
} }

View File

@@ -5100,9 +5100,9 @@
} }
}, },
"is-callable": { "is-callable": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.5.tgz", "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.6.tgz",
"integrity": "sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw==", "integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==",
"dev": true "dev": true
}, },
"is-ci": { "is-ci": {

View File

@@ -1,205 +0,0 @@
/* eslint-disable */
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const StatsPlugin = require('stats-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const theme = require('./theme');
var cwd = process.cwd();
const path = require('path');
const isProd = process.env.NODE_ENV === 'production';
const babelOptions = {
cacheDirectory: true,
babelrc: false,
presets: [require.resolve('@babel/preset-env'), require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-react')],
plugins: [
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
[require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-methods'), { loose: true }],
require.resolve('@babel/plugin-proposal-export-default-from'),
require.resolve('@babel/plugin-proposal-export-namespace-from'),
require.resolve('@babel/plugin-proposal-object-rest-spread'),
require.resolve('@babel/plugin-transform-runtime'),
require.resolve('@babel/plugin-proposal-optional-chaining'), //
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'), // 解决 ?? 无法转义问题
require.resolve('@babel/plugin-proposal-numeric-separator'), // 转义 1_000_000
!isProd && require.resolve('react-refresh/babel'),
]
.filter(Boolean)
.concat([
[
'babel-plugin-import',
{
libraryName: 'antd',
style: true,
},
],
'@babel/plugin-transform-object-assign',
]),
};
module.exports = () => {
const manifestName = `manifest.json`;
const cssFileName = isProd ? '[name]-[chunkhash].css' : '[name].css';
const plugins = [
new ProgressBarPlugin(),
new CaseSensitivePathsPlugin(),
new MiniCssExtractPlugin({
filename: cssFileName,
}),
new StatsPlugin(manifestName, {
chunkModules: false,
source: true,
chunks: false,
modules: false,
assets: true,
children: false,
exclude: [/node_modules/],
}),
new HappyPack({
id: 'babel',
loaders: [
'cache-loader',
{
loader: 'babel-loader',
options: babelOptions,
},
],
threadPool: happyThreadPool,
}),
!isProd &&
new ReactRefreshWebpackPlugin({
overlay: false,
}),
// new BundleAnalyzerPlugin({
// analyzerPort: 8889
// }),
].filter(Boolean);
if (isProd) {
plugins.push(new CleanWebpackPlugin());
}
return {
externals: isProd
? [
/^react$/,
/^react\/lib.*/,
/^react-dom$/,
/.*react-dom.*/,
/^single-spa$/,
/^single-spa-react$/,
/^moment$/,
/^antd$/,
/^lodash$/,
/^react-router$/,
/^react-router-dom$/,
]
: [],
resolve: {
symlinks: false,
extensions: ['.web.jsx', '.web.js', '.ts', '.tsx', '.js', '.jsx', '.json'],
alias: {
// '@pkgs': path.resolve(cwd, 'src/packages'),
'@pkgs': path.resolve(cwd, './node_modules/@didi/d1-packages'),
'@cpts': path.resolve(cwd, 'src/components'),
'@interface': path.resolve(cwd, 'src/interface'),
'@apis': path.resolve(cwd, 'src/api'),
react: path.resolve('./node_modules/react'),
actions: path.resolve(cwd, 'src/actions'),
lib: path.resolve(cwd, 'src/lib'),
constants: path.resolve(cwd, 'src/constants'),
components: path.resolve(cwd, 'src/components'),
container: path.resolve(cwd, 'src/container'),
api: path.resolve(cwd, 'src/api'),
assets: path.resolve(cwd, 'src/assets'),
mobxStore: path.resolve(cwd, 'src/mobxStore'),
},
},
plugins,
module: {
rules: [
{
parser: { system: false },
},
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules\/(?!react-intl|@didi\/dcloud-design)/,
use: [
{
loader: 'happypack/loader?id=babel',
},
],
},
{
test: /\.(png|svg|jpeg|jpg|gif|ttf|woff|woff2|eot|pdf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: './assets/image/',
esModule: false,
},
},
],
},
{
test: /\.(css|less)$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
},
],
},
optimization: Object.assign(
{
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
name: 'vendor',
priority: 10,
enforce: true,
minChunks: 1,
maxSize: 3500000,
},
},
},
},
isProd
? {
minimizer: [
new TerserJSPlugin({
cache: true,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin({}),
],
}
: {}
),
devtool: isProd ? 'cheap-module-source-map' : 'source-map',
node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
},
};
};

View File

@@ -0,0 +1,132 @@
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const StatsPlugin = require('stats-webpack-plugin');
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
const theme = require('./theme');
const pkgJson = require('../package');
const devMode = process.env.NODE_ENV === 'development';
const babelOptions = {
cacheDirectory: true,
babelrc: false,
presets: [require.resolve('@babel/preset-env'), require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-react')],
plugins: [
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
[require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-methods'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-property-in-object'), { loose: true }],
require.resolve('@babel/plugin-proposal-export-default-from'),
require.resolve('@babel/plugin-proposal-export-namespace-from'),
require.resolve('@babel/plugin-proposal-object-rest-spread'),
require.resolve('@babel/plugin-transform-runtime'),
require.resolve('@babel/plugin-proposal-optional-chaining'), //
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'), // 解决 ?? 无法转义问题
require.resolve('@babel/plugin-proposal-numeric-separator'), // 转义 1_000_000
devMode && require.resolve('react-refresh/babel'),
].filter(Boolean),
};
module.exports = {
entry: {
[pkgJson.ident]: ['./src/index.tsx'],
},
resolve: {
symlinks: false,
extensions: ['.web.jsx', '.web.js', '.ts', '.tsx', '.js', '.jsx', '.json'],
alias: {
'@src': path.resolve(process.cwd(), 'src'),
},
},
plugins: [
new ProgressBarPlugin(),
new CaseSensitivePathsPlugin(),
new StatsPlugin('manifest.json', {
chunkModules: false,
source: true,
chunks: false,
modules: false,
assets: true,
children: false,
exclude: [/node_modules/],
}),
new HappyPack({
id: 'babel',
loaders: [
'cache-loader',
{
loader: 'babel-loader',
options: babelOptions,
},
],
threadPool: happyThreadPool,
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
RUN_ENV: JSON.stringify(process.env.RUN_ENV),
},
}),
new HtmlWebpackPlugin({
meta: {
manifest: 'manifest.json',
},
template: './src/index.html',
inject: 'body',
}),
].filter(Boolean),
module: {
rules: [
{
parser: { system: false },
},
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules\/(?!react-intl|@didi\/dcloud-design)/,
use: [
{
loader: 'happypack/loader?id=babel',
},
],
},
{
test: /\.(png|svg|jpeg|jpg|gif|ttf|woff|woff2|eot|pdf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: './assets/image/',
esModule: false,
},
},
],
},
{
test: /\.(css|less)$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
},
],
},
node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
},
stats: 'errors-warnings',
};

View File

@@ -0,0 +1,35 @@
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const pkgJson = require('../package');
module.exports = {
mode: 'development',
plugins: [
new MiniCssExtractPlugin(),
new ReactRefreshWebpackPlugin({
overlay: false,
}),
],
devServer: {
host: '127.0.0.1',
port: pkgJson.port,
hot: true,
open: false,
publicPath: `http://localhost:${pkgJson.port}/${pkgJson.ident}/`,
inline: true,
disableHostCheck: true,
historyApiFallback: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
output: {
path: '/',
publicPath: `http://localhost:${pkgJson.port}/${pkgJson.ident}/`,
library: pkgJson.ident,
libraryTarget: 'amd',
filename: '[name].js',
chunkFilename: '[name].js',
},
devtool: 'cheap-module-eval-source-map',
};

View File

@@ -0,0 +1,59 @@
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const pkgJson = require('../package');
module.exports = {
mode: 'production',
externals: [
/^react$/,
/^react\/lib.*/,
/^react-dom$/,
/.*react-dom.*/,
/^single-spa$/,
/^single-spa-react$/,
/^moment$/,
/^lodash$/,
/^react-router$/,
/^react-router-dom$/,
],
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name]-[chunkhash].css',
}),
],
output: {
path: path.resolve(process.cwd(), `../../../km-rest/src/main/resources/templates/${pkgJson.ident}`),
publicPath: `${process.env.PUBLIC_PATH}/${pkgJson.ident}/`,
library: pkgJson.ident,
libraryTarget: 'amd',
filename: '[name]-[chunkhash].js',
chunkFilename: '[name]-[chunkhash].js',
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
name: 'vendor',
priority: 10,
enforce: true,
minChunks: 1,
maxSize: 3500000,
},
},
},
minimizer: [
new TerserJSPlugin({
cache: true,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin({}),
],
},
devtool: 'none',
};

View File

@@ -1344,6 +1344,16 @@
"@jridgewell/sourcemap-codec": "^1.4.10" "@jridgewell/sourcemap-codec": "^1.4.10"
} }
}, },
"@knowdesign/icons": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/@knowdesign/icons/-/icons-1.0.0.tgz",
"integrity": "sha512-7c+h2TSbh2ihTkXIivuO+DddNC5wG7hVv9SS4ccmkvTKls2ZTLitPu+U0wpufDxPhkPMaKEQfsECsVJ+7jLMiw==",
"requires": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons": "^4.7.0",
"react": "16.12.0"
}
},
"@nodelib/fs.scandir": { "@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -6815,9 +6825,9 @@
"dev": true "dev": true
}, },
"is-callable": { "is-callable": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.5.tgz", "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.6.tgz",
"integrity": "sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw==", "integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==",
"dev": true "dev": true
}, },
"is-color-stop": { "is-color-stop": {

View File

@@ -21,9 +21,11 @@
"build": "cross-env NODE_ENV=production webpack --max_old_space_size=8000" "build": "cross-env NODE_ENV=production webpack --max_old_space_size=8000"
}, },
"dependencies": { "dependencies": {
"@knowdesign/icons": "^1.0.0",
"babel-preset-react-app": "^10.0.0", "babel-preset-react-app": "^10.0.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"knowdesign": "1.3.7",
"less": "^3.9.0", "less": "^3.9.0",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"mobx": "4.15.7", "mobx": "4.15.7",
@@ -36,8 +38,7 @@
"react-intl": "^3.2.1", "react-intl": "^3.2.1",
"react-router-cache-route": "^1.11.1", "react-router-cache-route": "^1.11.1",
"single-spa": "^5.8.0", "single-spa": "^5.8.0",
"single-spa-react": "^2.14.0", "single-spa-react": "^2.14.0"
"knowdesign": "1.3.7"
}, },
"devDependencies": { "devDependencies": {
"@ant-design/icons": "^4.6.2", "@ant-design/icons": "^4.6.2",

View File

@@ -22,6 +22,20 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin-bottom: 12px; margin-bottom: 12px;
.left,
.right {
display: flex;
align-items: center;
}
.left .refresh-icon {
font-size: 20px;
color: #74788d;
cursor: pointer;
}
.right .search-input {
width: 248px;
margin-right: 8px;
}
} }
} }
} }

View File

@@ -47,8 +47,8 @@ serviceInstance.interceptors.response.use(
return res; return res;
}, },
(err: any) => { (err: any) => {
const config = err.config; const config = err?.config;
if (!config || !config.retryTimes) return dealResponse(err, config.customNotification); if (!config || !config.retryTimes) return dealResponse(err);
const { __retryCount = 0, retryDelay = 300, retryTimes } = config; const { __retryCount = 0, retryDelay = 300, retryTimes } = config;
config.__retryCount = __retryCount; config.__retryCount = __retryCount;
if (__retryCount >= retryTimes) { if (__retryCount >= retryTimes) {

View File

@@ -1,6 +1,6 @@
import React, { useLayoutEffect } from 'react'; import React, { useLayoutEffect } from 'react';
import { Utils, AppContainer } from 'knowdesign'; import { Utils, AppContainer } from 'knowdesign';
import { goLogin } from 'constants/axiosConfig'; import { goLogin } from '@src/constants/axiosConfig';
// 权限对应表 // 权限对应表
export enum ConfigPermissionMap { export enum ConfigPermissionMap {

View File

@@ -15,6 +15,7 @@ import {
AppContainer, AppContainer,
Utils, Utils,
} from 'knowdesign'; } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import moment from 'moment'; import moment from 'moment';
// 引入代码编辑器 // 引入代码编辑器
@@ -26,8 +27,8 @@ import 'codemirror/addon/selection/active-line';
import 'codemirror/addon/edit/closebrackets'; import 'codemirror/addon/edit/closebrackets';
require('codemirror/mode/xml/xml'); require('codemirror/mode/xml/xml');
require('codemirror/mode/javascript/javascript'); require('codemirror/mode/javascript/javascript');
import api from 'api'; import api from '@src/api';
import { defaultPagination } from 'constants/common'; import { defaultPagination } from '@src/constants/common';
import TypicalListCard from '../../components/TypicalListCard'; import TypicalListCard from '../../components/TypicalListCard';
import { ConfigPermissionMap } from '../CommonConfig'; import { ConfigPermissionMap } from '../CommonConfig';
import { ConfigOperate, ConfigProps } from './config'; import { ConfigOperate, ConfigProps } from './config';
@@ -384,7 +385,7 @@ export default () => {
const onDelete = (record: ConfigProps) => { const onDelete = (record: ConfigProps) => {
confirm({ confirm({
title: '确定删除配置吗?', title: '确定删除配置吗?',
content: `配置${record.valueName}${record.status === 1 ? '为启用状态,无法删除' : ''}`, content: `配置 [${record.valueName}] ${record.status === 1 ? '为启用状态,无法删除' : ''}`,
centered: true, centered: true,
okText: '删除', okText: '删除',
okType: 'primary', okType: 'primary',
@@ -398,9 +399,11 @@ export default () => {
}, },
maskClosable: true, maskClosable: true,
onOk() { onOk() {
return request(api.editConfig, { return request(api.delConfig, {
method: 'POST', method: 'DELETE',
data: record.id, params: {
id: record.id,
},
}).then((_) => { }).then((_) => {
message.success('删除成功'); message.success('删除成功');
getConfigList(); getConfigList();
@@ -431,22 +434,28 @@ export default () => {
<TypicalListCard title="配置管理"> <TypicalListCard title="配置管理">
<div className="config-manage-page"> <div className="config-manage-page">
<div className="operate-bar"> <div className="operate-bar">
<Form form={form} layout="inline" onFinish={() => getConfigList({ page: 1 })}> <div className="left">
<Form.Item name="valueGroup"> <div className="refresh-icon" onClick={() => getConfigList()}>
<Select style={{ width: 180 }} placeholder="请选择模块" options={configGroupList} /> <IconFont className="icon" type="icon-shuaxin1" />
</Form.Item> </div>
<Form.Item name="valueName"> <Divider type="vertical" style={{ height: 20, top: 0 }} />
<Input style={{ width: 180 }} placeholder="请输入配置键" /> <Form form={form} layout="inline" onFinish={() => getConfigList({ page: 1 })}>
</Form.Item> <Form.Item name="valueGroup">
<Form.Item name="memo"> <Select style={{ width: 180 }} placeholder="请选择模块" options={configGroupList} />
<Input style={{ width: 180 }} placeholder="请输入描述" /> </Form.Item>
</Form.Item> <Form.Item name="valueName">
<Form.Item> <Input style={{ width: 180 }} placeholder="请输入配置键" />
<Button type="primary" ghost htmlType="submit"> </Form.Item>
<Form.Item name="memo">
</Button> <Input style={{ width: 180 }} placeholder="请输入描述" />
</Form.Item> </Form.Item>
</Form> <Form.Item>
<Button type="primary" ghost htmlType="submit">
</Button>
</Form.Item>
</Form>
</div>
{global.hasPermission && global.hasPermission(ConfigPermissionMap.CONFIG_ADD) ? ( {global.hasPermission && global.hasPermission(ConfigPermissionMap.CONFIG_ADD) ? (
<Button <Button
type="primary" type="primary"

View File

@@ -1,7 +1,8 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Button, Form, Input, Select, ProTable, DatePicker, Utils, Tooltip } from 'knowdesign'; import { Button, Form, Input, Select, ProTable, DatePicker, Utils, Tooltip, Divider } from 'knowdesign';
import api from 'api'; import { IconFont } from '@knowdesign/icons';
import { defaultPagination } from 'constants/common'; import api from '@src/api';
import { defaultPagination } from '@src/constants/common';
import TypicalListCard from '../../components/TypicalListCard'; import TypicalListCard from '../../components/TypicalListCard';
import './index.less'; import './index.less';
import moment from 'moment'; import moment from 'moment';
@@ -119,25 +120,32 @@ export default () => {
<> <>
<TypicalListCard title="操作记录"> <TypicalListCard title="操作记录">
<div className="operate-bar"> <div className="operate-bar">
<Form form={form} layout="inline" onFinish={() => getData({ page: 1 })}> <div className="left">
<Form.Item name="targetType"> <div className="refresh-icon" onClick={() => getData()}>
<Select placeholder="请选择模块" options={configGroupList} style={{ width: 160 }} /> <IconFont className="icon" type="icon-shuaxin1" />
</Form.Item> </div>
<Form.Item name="target"> <Divider type="vertical" style={{ height: 20, top: 0 }} />
<Input placeholder="请输入操作对象" />
</Form.Item> <Form form={form} layout="inline" onFinish={() => getData({ page: 1 })}>
<Form.Item name="detail"> <Form.Item name="targetType">
<Input placeholder="请输入操作内容" /> <Select placeholder="请选择模块" options={configGroupList} style={{ width: 160 }} />
</Form.Item> </Form.Item>
<Form.Item name="time"> <Form.Item name="target">
<RangePicker showTime /> <Input placeholder="请输入操作对象" />
</Form.Item> </Form.Item>
<Form.Item> <Form.Item name="detail">
<Button type="primary" ghost htmlType="submit"> <Input placeholder="请输入操作内容" />
</Form.Item>
</Button> <Form.Item name="time">
</Form.Item> <RangePicker showTime />
</Form> </Form.Item>
<Form.Item>
<Button type="primary" ghost htmlType="submit">
</Button>
</Form.Item>
</Form>
</div>
</div> </div>
<ProTable <ProTable

View File

@@ -21,9 +21,9 @@ import {
} from 'knowdesign'; } from 'knowdesign';
import moment from 'moment'; import moment from 'moment';
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons'; import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
import { defaultPagination } from 'constants/common'; import { defaultPagination } from '@src/constants/common';
import { RoleProps, PermissionNode, AssignUser, RoleOperate, FormItemPermission } from './config'; import { RoleProps, PermissionNode, AssignUser, RoleOperate, FormItemPermission } from './config';
import api from 'api'; import api from '@src/api';
import CheckboxGroupContainer from './CheckboxGroupContainer'; import CheckboxGroupContainer from './CheckboxGroupContainer';
import { ConfigPermissionMap } from '../CommonConfig'; import { ConfigPermissionMap } from '../CommonConfig';
@@ -611,38 +611,45 @@ export default (props: { curTabKey: string }): JSX.Element => {
return ( return (
<> <>
<div className="operate-bar-right"> <div className="operate-bar">
<Input <div className="left">
className="search-input" <div className="refresh-icon" onClick={() => getRoleList()}>
suffix={ <IconFont className="icon" type="icon-shuaxin1" />
<IconFont </div>
type="icon-fangdajing" </div>
onClick={(_) => { <div className="right">
setSearchKeywords(searchKeywordsInput); <Input
}} className="search-input"
style={{ fontSize: '16px' }} suffix={
/> <IconFont
} type="icon-fangdajing"
placeholder="请输入角色名称" onClick={(_) => {
value={searchKeywordsInput} setSearchKeywords(searchKeywordsInput);
onPressEnter={(_) => { }}
setSearchKeywords(searchKeywordsInput); style={{ fontSize: '16px' }}
}} />
onChange={(e) => { }
setSearchKeywordsInput(e.target.value); placeholder="请输入角色名称"
}} value={searchKeywordsInput}
/> onPressEnter={(_) => {
{global.hasPermission && global.hasPermission(ConfigPermissionMap.ROLE_ADD) ? ( setSearchKeywords(searchKeywordsInput);
<Button }}
type="primary" onChange={(e) => {
icon={<PlusOutlined />} setSearchKeywordsInput(e.target.value);
onClick={() => detailRef.current.onOpen(true, RoleOperate.Add, getRoleList, undefined)} }}
> />
{global.hasPermission && global.hasPermission(ConfigPermissionMap.ROLE_ADD) ? (
</Button> <Button
) : ( type="primary"
<></> icon={<PlusOutlined />}
)} onClick={() => detailRef.current.onOpen(true, RoleOperate.Add, getRoleList, undefined)}
>
</Button>
) : (
<></>
)}
</div>
</div> </div>
<ProTable <ProTable

View File

@@ -1,12 +1,13 @@
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'; import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { Form, ProTable, Select, Button, Input, Modal, message, Drawer, Space, Divider, AppContainer, Utils } from 'knowdesign'; import { Form, ProTable, Select, Button, Input, Modal, message, Drawer, Space, Divider, AppContainer, Utils } from 'knowdesign';
import { IconFont } from '@knowdesign/icons';
import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import moment from 'moment'; import moment from 'moment';
import { defaultPagination } from 'constants/common'; import { defaultPagination } from '@src/constants/common';
import { UserProps, UserOperate } from './config'; import { UserProps, UserOperate } from './config';
import CheckboxGroupContainer from './CheckboxGroupContainer'; import CheckboxGroupContainer from './CheckboxGroupContainer';
import TagsWithHide from '../../components/TagsWithHide/index'; import TagsWithHide from '../../components/TagsWithHide/index';
import api from 'api'; import api from '@src/api';
import { ConfigPermissionMap } from '../CommonConfig'; import { ConfigPermissionMap } from '../CommonConfig';
const { confirm } = Modal; const { confirm } = Modal;
@@ -341,22 +342,29 @@ export default (props: { curTabKey: string }) => {
return ( return (
<> <>
<div className="operate-bar"> <div className="operate-bar">
<Form form={form} layout="inline" onFinish={() => getUserList({ page: 1 })}> <div className="left">
<Form.Item name="userName"> <div className="refresh-icon" onClick={() => getUserList()}>
<Input placeholder="请输入用户账号" /> <IconFont className="icon" type="icon-shuaxin1" />
</Form.Item> </div>
<Form.Item name="realName"> <Divider type="vertical" style={{ height: 20, top: 0 }} />
<Input placeholder="请输入用户实名" />
</Form.Item> <Form form={form} layout="inline" onFinish={() => getUserList({ page: 1 })}>
<Form.Item name="roleId"> <Form.Item name="userName">
<Select style={{ width: 190 }} placeholder="选择平台已创建的角色名" options={simpleRoleList} /> <Input placeholder="请输入用户账号" />
</Form.Item> </Form.Item>
<Form.Item> <Form.Item name="realName">
<Button type="primary" ghost htmlType="submit"> <Input placeholder="请输入用户实名" />
</Form.Item>
</Button> <Form.Item name="roleId">
</Form.Item> <Select style={{ width: 190 }} placeholder="选择平台已创建的角色名" options={simpleRoleList} />
</Form> </Form.Item>
<Form.Item>
<Button type="primary" ghost htmlType="submit">
</Button>
</Form.Item>
</Form>
</div>
{global.hasPermission && global.hasPermission(ConfigPermissionMap.USER_ADD) ? ( {global.hasPermission && global.hasPermission(ConfigPermissionMap.USER_ADD) ? (
<Button <Button
type="primary" type="primary"

View File

@@ -44,13 +44,3 @@
.role-tab-assign-user .desc-row { .role-tab-assign-user .desc-row {
margin-bottom: 24px; margin-bottom: 24px;
} }
.operate-bar-right {
display: flex;
justify-content: right;
margin-bottom: 12px;
.search-input {
width: 248px;
margin-right: 8px;
}
}

View File

@@ -1,56 +1,9 @@
/* eslint-disable */
const path = require('path'); const path = require('path');
require('dotenv').config({ path: path.resolve(process.cwd(), '../../.env') }); require('dotenv').config({ path: path.resolve(process.cwd(), '../../.env') });
const isProd = process.env.NODE_ENV === 'production';
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const merge = require('webpack-merge'); const merge = require('webpack-merge');
const pkgJson = require('./package'); const devMode = process.env.NODE_ENV === 'development';
const getWebpackCommonConfig = require('./config/d1-webpack.base'); const commonConfig = require('./config/webpack.common');
const outPath = path.resolve(__dirname, `../../../km-rest/src/main/resources/templates/${pkgJson.ident}`); const devConfig = require('./config/webpack.dev');
const jsFileName = isProd ? '[name]-[chunkhash].js' : '[name].js'; const prodConfig = require('./config/webpack.prod');
module.exports = merge(getWebpackCommonConfig(), { module.exports = merge(commonConfig, devMode ? devConfig : prodConfig);
mode: isProd ? 'production' : 'development',
entry: {
[pkgJson.ident]: ['./src/index.tsx'],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
RUN_ENV: JSON.stringify(process.env.RUN_ENV),
},
}),
new HtmlWebpackPlugin({
meta: {
manifest: 'manifest.json',
},
template: './src/index.html',
inject: 'body',
}),
],
output: {
path: outPath,
publicPath: isProd ? `${process.env.PUBLIC_PATH}/${pkgJson.ident}/` : `http://localhost:${pkgJson.port}/${pkgJson.ident}/`,
library: pkgJson.ident,
libraryTarget: 'amd',
filename: jsFileName,
chunkFilename: jsFileName,
},
devtool: isProd ? 'none' : 'cheap-module-eval-source-map',
devServer: {
host: '127.0.0.1',
port: pkgJson.port,
hot: true,
open: false,
publicPath: `http://localhost:${pkgJson.port}/${pkgJson.ident}/`,
inline: true,
disableHostCheck: true,
historyApiFallback: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
proxy: {},
},
});

View File

@@ -1,183 +0,0 @@
/* eslint-disable */
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CoverHtmlWebpackPlugin = require('./CoverHtmlWebpackPlugin.js');
var webpackConfigResolveAlias = require('./webpackConfigResolveAlias');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const theme = require('./theme');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const isProd = process.env.NODE_ENV === 'production';
const babelOptions = {
cacheDirectory: true,
babelrc: false,
presets: [require.resolve('@babel/preset-env'), require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-react')],
plugins: [
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
[require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-property-in-object'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-methods'), { loose: true }],
require.resolve('@babel/plugin-proposal-export-default-from'),
require.resolve('@babel/plugin-proposal-export-namespace-from'),
require.resolve('@babel/plugin-proposal-object-rest-spread'),
require.resolve('@babel/plugin-transform-runtime'),
!isProd && require.resolve('react-refresh/babel'),
]
.filter(Boolean)
.concat([
[
'babel-plugin-import',
{
libraryName: 'antd',
style: true,
},
],
'@babel/plugin-transform-object-assign',
]),
};
module.exports = () => {
const cssFileName = isProd ? '[name]-[chunkhash].css' : '[name].css';
const plugins = [
new CoverHtmlWebpackPlugin(),
new ProgressBarPlugin(),
new CaseSensitivePathsPlugin(),
new MiniCssExtractPlugin({
filename: cssFileName,
}),
!isProd &&
new ReactRefreshWebpackPlugin({
overlay: false,
}),
].filter(Boolean);
const resolve = {
symlinks: false,
extensions: ['.web.jsx', '.web.js', '.ts', '.tsx', '.js', '.jsx', '.json'],
alias: webpackConfigResolveAlias,
};
if (isProd) {
plugins.push(new CleanWebpackPlugin());
}
if (!isProd) {
resolve.mainFields = ['module', 'browser', 'main'];
}
return {
externals: isProd
? [
/^react$/,
/^react\/lib.*/,
/^react-dom$/,
/.*react-dom.*/,
/^single-spa$/,
/^single-spa-react$/,
/^moment$/,
/^antd$/,
/^lodash$/,
/^echarts$/,
/^react-router$/,
/^react-router-dom$/,
]
: [],
resolve,
plugins,
module: {
rules: [
{
parser: { system: false },
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: babelOptions,
},
],
},
{
test: /\.(ts|tsx)$/,
use: [
{
loader: 'babel-loader',
options: babelOptions,
},
{
loader: 'ts-loader',
options: {
allowTsInNodeModules: true,
},
},
],
},
{
test: /\.(png|svg|jpeg|jpg|gif|ttf|woff|woff2|eot|pdf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: './assets/image/',
esModule: false,
},
},
],
},
{
test: /\.(css|less)$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
},
],
},
optimization: Object.assign(
isProd
? {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
name: 'vendor',
priority: 10,
enforce: true,
minChunks: 1,
maxSize: 3000000,
},
},
},
minimizer: [
new TerserJSPlugin({
cache: true,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin({}),
],
}
: {}
),
devtool: isProd ? 'cheap-module-source-map' : '',
node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
},
};
};

View File

@@ -0,0 +1,123 @@
const path = require('path');
const theme = require('./theme');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const CoverHtmlWebpackPlugin = require('./CoverHtmlWebpackPlugin.js');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const devMode = process.env.NODE_ENV === 'development';
const babelOptions = {
cacheDirectory: true,
babelrc: false,
presets: [require.resolve('@babel/preset-env'), require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-react')],
plugins: [
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
[require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-property-in-object'), { loose: true }],
[require.resolve('@babel/plugin-proposal-private-methods'), { loose: true }],
require.resolve('@babel/plugin-proposal-export-default-from'),
require.resolve('@babel/plugin-proposal-export-namespace-from'),
require.resolve('@babel/plugin-proposal-object-rest-spread'),
require.resolve('@babel/plugin-transform-runtime'),
devMode && require.resolve('react-refresh/babel'),
devMode && [
'babel-plugin-import',
{
libraryName: 'antd',
style: true,
},
],
].filter(Boolean),
};
module.exports = {
entry: {
layout: ['./src/index.tsx'],
},
resolve: {
symlinks: false,
extensions: ['.web.jsx', '.web.js', '.ts', '.tsx', '.js', '.jsx', '.json'],
alias: {
'@src': path.resolve('src'),
},
},
plugins: [
new CoverHtmlWebpackPlugin(),
new ProgressBarPlugin(),
new CaseSensitivePathsPlugin(),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
RUN_ENV: JSON.stringify(process.env.RUN_ENV),
BUSINESS_VERSION: process.env.BUSINESS_VERSION === 'true',
PUBLIC_PATH: JSON.stringify(process.env.PUBLIC_PATH),
},
}),
new HtmlWebpackPlugin({
meta: {
manifest: 'manifest.json',
},
template: './src/index.html',
favicon: path.resolve('favicon.ico'),
inject: 'body',
}),
],
module: {
rules: [
{
parser: { system: false },
},
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: babelOptions,
},
{
loader: 'ts-loader',
options: {
allowTsInNodeModules: true,
},
},
],
},
{
test: /\.(png|svg|jpeg|jpg|gif|ttf|woff|woff2|eot|pdf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: './assets/image/',
esModule: false,
},
},
],
},
{
test: /\.(css|less)$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true,
modifyVars: theme,
},
},
],
},
],
},
node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
},
stats: 'errors-warnings',
};

View File

@@ -0,0 +1,45 @@
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
mode: 'development',
plugins: [
new MiniCssExtractPlugin(),
new ReactRefreshWebpackPlugin({
overlay: false,
}),
],
output: {
path: '/',
publicPath: '/',
filename: '[name].js',
chunkFilename: '[name].js',
library: 'layout',
libraryTarget: 'amd',
},
devServer: {
host: 'localhost',
port: 8000,
hot: true,
open: true,
openPage: 'http://localhost:8000/',
inline: true,
historyApiFallback: true,
publicPath: `http://localhost:8000/`,
headers: {
'cache-control': 'no-cache',
pragma: 'no-cache',
'Access-Control-Allow-Origin': '*',
},
proxy: {
'/ks-km/api/v3': {
changeOrigin: true,
target: 'http://localhost:8080/',
},
'/logi-security/api/v1': {
changeOrigin: true,
target: 'http://localhost:8080/',
},
},
},
};

View File

@@ -0,0 +1,79 @@
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CountPlugin = require('./CountComponentWebpackPlugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const outputPath = path.resolve(process.cwd(), `../../../km-rest/src/main/resources/templates/layout`);
module.exports = {
mode: 'production',
plugins: [
new CleanWebpackPlugin(),
new CountPlugin({
pathname: 'knowdesign',
startCount: true,
isExportExcel: false,
}),
new MiniCssExtractPlugin({
filename: '[name]-[chunkhash].css',
}),
new CopyWebpackPlugin([
{
from: path.resolve(process.cwd(), 'static'),
to: path.resolve(outputPath, '../static'),
},
{
from: path.resolve(process.cwd(), 'favicon.ico'),
to: path.resolve(outputPath, '../favicon.ico'),
},
]),
],
externals: [
/^react$/,
/^react\/lib.*/,
/^react-dom$/,
/.*react-dom.*/,
/^single-spa$/,
/^single-spa-react$/,
/^moment$/,
/^antd$/,
/^lodash$/,
/^echarts$/,
/^react-router$/,
/^react-router-dom$/,
],
output: {
path: outputPath,
publicPath: process.env.PUBLIC_PATH + '/layout/',
filename: '[name]-[chunkhash].js',
chunkFilename: '[name]-[chunkhash].js',
library: 'layout',
libraryTarget: 'amd',
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
name: 'vendor',
priority: 10,
enforce: true,
minChunks: 1,
maxSize: 3000000,
},
},
},
minimizer: [
new TerserJSPlugin({
cache: true,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin({}),
],
},
devtool: 'none',
};

View File

@@ -1,5 +0,0 @@
var path = require('path');
module.exports = {
react: path.resolve('./node_modules/react'),
};

View File

@@ -6024,6 +6024,12 @@
} }
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -6562,6 +6568,12 @@
"kind-of": "^4.0.0" "kind-of": "^4.0.0"
}, },
"dependencies": { "dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -6638,18 +6650,6 @@
"resolved": "https://registry.npmmirror.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", "resolved": "https://registry.npmmirror.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
"integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ=="
}, },
"hastscript": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/hastscript/-/hastscript-6.0.0.tgz",
"integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
"requires": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^1.0.0",
"hast-util-parse-selector": "^2.0.0",
"property-information": "^5.0.0",
"space-separated-tokens": "^1.0.0"
}
},
"he": { "he": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz",
@@ -6666,11 +6666,6 @@
"resolved": "https://registry.npmmirror.com/highlight-words-core/-/highlight-words-core-1.2.2.tgz", "resolved": "https://registry.npmmirror.com/highlight-words-core/-/highlight-words-core-1.2.2.tgz",
"integrity": "sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg==" "integrity": "sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg=="
}, },
"highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
},
"history": { "history": {
"version": "4.10.1", "version": "4.10.1",
"resolved": "https://registry.npmmirror.com/history/-/history-4.10.1.tgz", "resolved": "https://registry.npmmirror.com/history/-/history-4.10.1.tgz",
@@ -6883,6 +6878,12 @@
} }
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -7224,28 +7225,6 @@
"integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
"dev": true "dev": true
}, },
"intl-format-cache": {
"version": "4.3.1",
"resolved": "https://registry.npmmirror.com/intl-format-cache/-/intl-format-cache-4.3.1.tgz",
"integrity": "sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q=="
},
"intl-messageformat": {
"version": "7.8.4",
"resolved": "https://registry.npmmirror.com/intl-messageformat/-/intl-messageformat-7.8.4.tgz",
"integrity": "sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA==",
"requires": {
"intl-format-cache": "^4.2.21",
"intl-messageformat-parser": "^3.6.4"
}
},
"intl-messageformat-parser": {
"version": "3.6.4",
"resolved": "https://registry.npmmirror.com/intl-messageformat-parser/-/intl-messageformat-parser-3.6.4.tgz",
"integrity": "sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA==",
"requires": {
"@formatjs/intl-unified-numberformat": "^3.2.0"
}
},
"invariant": { "invariant": {
"version": "2.2.4", "version": "2.2.4",
"resolved": "https://registry.npmmirror.com/invariant/-/invariant-2.2.4.tgz", "resolved": "https://registry.npmmirror.com/invariant/-/invariant-2.2.4.tgz",
@@ -7287,6 +7266,12 @@
"kind-of": "^3.0.2" "kind-of": "^3.0.2"
}, },
"dependencies": { "dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -7354,16 +7339,10 @@
"has-tostringtag": "^1.0.0" "has-tostringtag": "^1.0.0"
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-callable": { "is-callable": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.5.tgz", "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.6.tgz",
"integrity": "sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw==" "integrity": "sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q=="
}, },
"is-color-stop": { "is-color-stop": {
"version": "1.1.0", "version": "1.1.0",
@@ -7396,6 +7375,12 @@
"kind-of": "^3.0.2" "kind-of": "^3.0.2"
}, },
"dependencies": { "dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -7531,12 +7516,6 @@
"path-is-inside": "^1.0.2" "path-is-inside": "^1.0.2"
} }
}, },
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
"integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
"dev": true
},
"is-plain-object": { "is-plain-object": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz", "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz",
@@ -7790,11 +7769,9 @@
"rc-dialog": "~8.6.0", "rc-dialog": "~8.6.0",
"rc-drawer": "^4.4.3", "rc-drawer": "^4.4.3",
"rc-dropdown": "~3.2.0", "rc-dropdown": "~3.2.0",
"rc-field-form": "~1.21.0",
"rc-image": "~5.2.5", "rc-image": "~5.2.5",
"rc-input-number": "~7.3.6", "rc-input-number": "~7.3.6",
"rc-mentions": "~1.6.1", "rc-mentions": "~1.6.1",
"rc-menu": "~9.0.12",
"rc-motion": "^2.4.4", "rc-motion": "^2.4.4",
"rc-notification": "~4.5.7", "rc-notification": "~4.5.7",
"rc-pagination": "~3.1.9", "rc-pagination": "~3.1.9",
@@ -7806,11 +7783,9 @@
"rc-slider": "~9.7.4", "rc-slider": "~9.7.4",
"rc-steps": "~4.1.0", "rc-steps": "~4.1.0",
"rc-switch": "~3.2.0", "rc-switch": "~3.2.0",
"rc-table": "~7.19.0",
"rc-tabs": "~11.10.0", "rc-tabs": "~11.10.0",
"rc-textarea": "~0.3.0", "rc-textarea": "~0.3.0",
"rc-tooltip": "~5.1.1", "rc-tooltip": "~5.1.1",
"rc-tree": "~5.3.0",
"rc-tree-select": "~4.8.0", "rc-tree-select": "~4.8.0",
"rc-trigger": "^5.2.10", "rc-trigger": "^5.2.10",
"rc-upload": "~4.3.0", "rc-upload": "~4.3.0",
@@ -7848,6 +7823,77 @@
"scroll-into-view-if-needed": "^2.2.25" "scroll-into-view-if-needed": "^2.2.25"
}, },
"dependencies": { "dependencies": {
"async-validator": {
"version": "4.2.5",
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
},
"intl-format-cache": {
"version": "2.2.9",
"resolved": "https://registry.npmmirror.com/intl-format-cache/-/intl-format-cache-2.2.9.tgz",
"integrity": "sha512-Zv/u8wRpekckv0cLkwpVdABYST4hZNTDaX7reFetrYTJwxExR2VyTqQm+l0WmL0Qo8Mjb9Tf33qnfj0T7pjxdQ=="
},
"intl-messageformat": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/intl-messageformat/-/intl-messageformat-2.2.0.tgz",
"integrity": "sha512-I+tSvHnXqJYjDfNmY95tpFMj30yoakC6OXAo+wu/wTMy6tA/4Fd4mvV7Uzs4cqK/Ap29sHhwjcY+78a8eifcXw==",
"requires": {
"intl-messageformat-parser": "1.4.0"
}
},
"intl-messageformat-parser": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz",
"integrity": "sha512-/XkqFHKezO6UcF4Av2/Lzfrez18R0jyw7kRFhSeB/YRakdrgSc9QfFZUwNJI9swMwMoNPygK1ArC5wdFSjPw+A=="
},
"rc-field-form": {
"version": "1.27.1",
"resolved": "https://registry.npmmirror.com/rc-field-form/-/rc-field-form-1.27.1.tgz",
"integrity": "sha512-RShegnwFu6TH8tl2olCxn+B4Wyh5EiQH8c/7wucbkLNyue05YiH5gomUAg1vbZjp71yFKwegClctsEG5CNBWAA==",
"requires": {
"@babel/runtime": "^7.18.0",
"async-validator": "^4.1.0",
"rc-util": "^5.8.0"
}
},
"rc-menu": {
"version": "9.6.4",
"resolved": "https://registry.npmmirror.com/rc-menu/-/rc-menu-9.6.4.tgz",
"integrity": "sha512-6DiNAjxjVIPLZXHffXxxcyE15d4isRL7iQ1ru4MqYDH2Cqc5bW96wZOdMydFtGLyDdnmEQ9jVvdCE9yliGvzkw==",
"requires": {
"@babel/runtime": "^7.10.1",
"classnames": "2.x",
"rc-motion": "^2.4.3",
"rc-overflow": "^1.2.0",
"rc-trigger": "^5.1.2",
"rc-util": "^5.12.0",
"shallowequal": "^1.1.0"
}
},
"rc-table": {
"version": "7.25.3",
"resolved": "https://registry.npmmirror.com/rc-table/-/rc-table-7.25.3.tgz",
"integrity": "sha512-McsLJ2rg8EEpRBRYN4Pf9gT7ZNYnjvF9zrBpUBBbUX/fxk+eGi5ff1iPIhMyiHsH71/BmTUzX9nc9XqupD0nMg==",
"requires": {
"@babel/runtime": "^7.10.1",
"classnames": "^2.2.5",
"rc-resize-observer": "^1.1.0",
"rc-util": "^5.22.5",
"shallowequal": "^1.1.0"
}
},
"rc-tree": {
"version": "5.6.9",
"resolved": "https://registry.npmmirror.com/rc-tree/-/rc-tree-5.6.9.tgz",
"integrity": "sha512-si8aGuWQ2/sh2Ibk+WdUdDeAxoviT/+kDY+NLtJ+RhqfySqPFqWM5uHTwgFRrWUvKCqEeE/PjCYuuhHrK7Y7+A==",
"requires": {
"@babel/runtime": "^7.10.1",
"classnames": "2.x",
"rc-motion": "^2.0.1",
"rc-util": "^5.16.1",
"rc-virtual-list": "^3.4.8"
}
},
"rc-util": { "rc-util": {
"version": "5.24.2", "version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz", "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
@@ -8287,6 +8333,13 @@
"requires": { "requires": {
"fault": "^1.0.0", "fault": "^1.0.0",
"highlight.js": "~10.7.0" "highlight.js": "~10.7.0"
},
"dependencies": {
"highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
}
} }
}, },
"lru-cache": { "lru-cache": {
@@ -8799,6 +8852,12 @@
"is-descriptor": "^0.1.0" "is-descriptor": "^0.1.0"
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -9092,19 +9151,6 @@
"safe-buffer": "^5.1.1" "safe-buffer": "^5.1.1"
} }
}, },
"parse-entities": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"requires": {
"character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0",
"character-reference-invalid": "^1.0.0",
"is-alphanumerical": "^1.0.0",
"is-decimal": "^1.0.0",
"is-hexadecimal": "^1.0.0"
}
},
"parse-json": { "parse-json": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz", "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
@@ -10243,33 +10289,6 @@
"rc-editor-core": "~0.8.3" "rc-editor-core": "~0.8.3"
} }
}, },
"rc-field-form": {
"version": "1.21.2",
"resolved": "https://registry.npmmirror.com/rc-field-form/-/rc-field-form-1.21.2.tgz",
"integrity": "sha512-LR/bURt/Tf5g39mb0wtMtQuWn42d/7kEzpzlC5fNC7yaRVmLTtlPP4sBBlaViETM9uZQKLoaB0Pt9Mubhm9gow==",
"requires": {
"@babel/runtime": "^7.8.4",
"async-validator": "^4.0.2",
"rc-util": "^5.8.0"
},
"dependencies": {
"async-validator": {
"version": "4.2.5",
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
},
"rc-util": {
"version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==",
"requires": {
"@babel/runtime": "^7.18.3",
"react-is": "^16.12.0",
"shallowequal": "^1.1.0"
}
}
}
},
"rc-form": { "rc-form": {
"version": "2.4.12", "version": "2.4.12",
"resolved": "https://registry.npmmirror.com/rc-form/-/rc-form-2.4.12.tgz", "resolved": "https://registry.npmmirror.com/rc-form/-/rc-form-2.4.12.tgz",
@@ -10370,32 +10389,6 @@
} }
} }
}, },
"rc-menu": {
"version": "9.0.14",
"resolved": "https://registry.npmmirror.com/rc-menu/-/rc-menu-9.0.14.tgz",
"integrity": "sha512-CIox5mZeLDAi32SlHrV7UeSjv7tmJJhwRyxQtZCKt351w3q59XlL4WMFOmtT9gwIfP9h0XoxdBZUMe/xzkp78A==",
"requires": {
"@babel/runtime": "^7.10.1",
"classnames": "2.x",
"rc-motion": "^2.4.3",
"rc-overflow": "^1.2.0",
"rc-trigger": "^5.1.2",
"rc-util": "^5.12.0",
"shallowequal": "^1.1.0"
},
"dependencies": {
"rc-util": {
"version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==",
"requires": {
"@babel/runtime": "^7.18.3",
"react-is": "^16.12.0",
"shallowequal": "^1.1.0"
}
}
}
},
"rc-motion": { "rc-motion": {
"version": "2.6.2", "version": "2.6.2",
"resolved": "https://registry.npmmirror.com/rc-motion/-/rc-motion-2.6.2.tgz", "resolved": "https://registry.npmmirror.com/rc-motion/-/rc-motion-2.6.2.tgz",
@@ -10648,30 +10641,6 @@
} }
} }
}, },
"rc-table": {
"version": "7.19.2",
"resolved": "https://registry.npmmirror.com/rc-table/-/rc-table-7.19.2.tgz",
"integrity": "sha512-NdpnoM50MK02H5/hGOsObfxCvGFUG5cHB9turE5BKJ81T5Ycbq193w5tLhnpILXe//Oanzr47MdMxkUnVGP+qg==",
"requires": {
"@babel/runtime": "^7.10.1",
"classnames": "^2.2.5",
"rc-resize-observer": "^1.0.0",
"rc-util": "^5.14.0",
"shallowequal": "^1.1.0"
},
"dependencies": {
"rc-util": {
"version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==",
"requires": {
"@babel/runtime": "^7.18.3",
"react-is": "^16.12.0",
"shallowequal": "^1.1.0"
}
}
}
},
"rc-tabs": { "rc-tabs": {
"version": "11.10.8", "version": "11.10.8",
"resolved": "https://registry.npmmirror.com/rc-tabs/-/rc-tabs-11.10.8.tgz", "resolved": "https://registry.npmmirror.com/rc-tabs/-/rc-tabs-11.10.8.tgz",
@@ -10744,30 +10713,6 @@
"rc-trigger": "^5.0.0" "rc-trigger": "^5.0.0"
} }
}, },
"rc-tree": {
"version": "5.3.8",
"resolved": "https://registry.npmmirror.com/rc-tree/-/rc-tree-5.3.8.tgz",
"integrity": "sha512-YuobEryPymqPmHFUOvsoOrYdm24psaj0CrGEUuDUQUeG/nNcTGw6FA2YmF4NsEaNBvNSJUSzwfZnFHrKa/xv0A==",
"requires": {
"@babel/runtime": "^7.10.1",
"classnames": "2.x",
"rc-motion": "^2.0.1",
"rc-util": "^5.16.1",
"rc-virtual-list": "^3.4.1"
},
"dependencies": {
"rc-util": {
"version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
"integrity": "sha512-MWd0ZEV7xSwN4HM9jz9BwpnMzwCPjYJ7K90lePsrdgAkrmm8U7b4BOTIsv/84BQsaF7N3ejNkcrZ3AfEwc9HXA==",
"requires": {
"@babel/runtime": "^7.18.3",
"react-is": "^16.12.0",
"shallowequal": "^1.1.0"
}
}
}
},
"rc-tree-select": { "rc-tree-select": {
"version": "4.8.0", "version": "4.8.0",
"resolved": "https://registry.npmmirror.com/rc-tree-select/-/rc-tree-select-4.8.0.tgz", "resolved": "https://registry.npmmirror.com/rc-tree-select/-/rc-tree-select-4.8.0.tgz",
@@ -10780,6 +10725,18 @@
"rc-util": "^5.7.0" "rc-util": "^5.7.0"
}, },
"dependencies": { "dependencies": {
"rc-tree": {
"version": "5.3.8",
"resolved": "https://registry.npmmirror.com/rc-tree/-/rc-tree-5.3.8.tgz",
"integrity": "sha512-YuobEryPymqPmHFUOvsoOrYdm24psaj0CrGEUuDUQUeG/nNcTGw6FA2YmF4NsEaNBvNSJUSzwfZnFHrKa/xv0A==",
"requires": {
"@babel/runtime": "^7.10.1",
"classnames": "2.x",
"rc-motion": "^2.0.1",
"rc-util": "^5.16.1",
"rc-virtual-list": "^3.4.1"
}
},
"rc-util": { "rc-util": {
"version": "5.24.2", "version": "5.24.2",
"resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz", "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.24.2.tgz",
@@ -11122,6 +11079,30 @@
"intl-messageformat": "^7.8.4", "intl-messageformat": "^7.8.4",
"intl-messageformat-parser": "^3.6.4", "intl-messageformat-parser": "^3.6.4",
"shallow-equal": "^1.2.1" "shallow-equal": "^1.2.1"
},
"dependencies": {
"intl-format-cache": {
"version": "4.3.1",
"resolved": "https://registry.npmmirror.com/intl-format-cache/-/intl-format-cache-4.3.1.tgz",
"integrity": "sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q=="
},
"intl-messageformat": {
"version": "7.8.4",
"resolved": "https://registry.npmmirror.com/intl-messageformat/-/intl-messageformat-7.8.4.tgz",
"integrity": "sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA==",
"requires": {
"intl-format-cache": "^4.2.21",
"intl-messageformat-parser": "^3.6.4"
}
},
"intl-messageformat-parser": {
"version": "3.6.4",
"resolved": "https://registry.npmmirror.com/intl-messageformat-parser/-/intl-messageformat-parser-3.6.4.tgz",
"integrity": "sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA==",
"requires": {
"@formatjs/intl-unified-numberformat": "^3.2.0"
}
}
} }
}, },
"react-is": { "react-is": {
@@ -11282,6 +11263,13 @@
"lowlight": "^1.14.0", "lowlight": "^1.14.0",
"prismjs": "^1.21.0", "prismjs": "^1.21.0",
"refractor": "^3.0.0" "refractor": "^3.0.0"
},
"dependencies": {
"highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
}
} }
}, },
"react-test-renderer": { "react-test-renderer": {
@@ -11449,6 +11437,31 @@
"prismjs": "~1.27.0" "prismjs": "~1.27.0"
}, },
"dependencies": { "dependencies": {
"hastscript": {
"version": "6.0.0",
"resolved": "https://registry.npmmirror.com/hastscript/-/hastscript-6.0.0.tgz",
"integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
"requires": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^1.0.0",
"hast-util-parse-selector": "^2.0.0",
"property-information": "^5.0.0",
"space-separated-tokens": "^1.0.0"
}
},
"parse-entities": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"requires": {
"character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0",
"character-reference-invalid": "^1.0.0",
"is-alphanumerical": "^1.0.0",
"is-decimal": "^1.0.0",
"is-hexadecimal": "^1.0.0"
}
},
"prismjs": { "prismjs": {
"version": "1.27.0", "version": "1.27.0",
"resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.27.0.tgz", "resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.27.0.tgz",
@@ -12286,6 +12299,12 @@
"kind-of": "^3.2.0" "kind-of": "^3.2.0"
}, },
"dependencies": { "dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -12347,6 +12366,14 @@
"dev": true, "dev": true,
"requires": { "requires": {
"is-plain-obj": "^1.0.0" "is-plain-obj": "^1.0.0"
},
"dependencies": {
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
"integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
"dev": true
}
} }
}, },
"source-list-map": { "source-list-map": {
@@ -13049,6 +13076,12 @@
"kind-of": "^3.0.2" "kind-of": "^3.0.2"
}, },
"dependencies": { "dependencies": {
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz",
@@ -13502,12 +13535,6 @@
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"dev": true "dev": true
}, },
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
},
"v8-compile-cache": { "v8-compile-cache": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
@@ -13718,6 +13745,13 @@
"binary-extensions": "^1.0.0" "binary-extensions": "^1.0.0"
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true,
"optional": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -13895,6 +13929,12 @@
} }
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -14338,6 +14378,12 @@
"binary-extensions": "^1.0.0" "binary-extensions": "^1.0.0"
} }
}, },
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-number": { "is-number": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz",
@@ -14439,6 +14485,14 @@
"requires": { "requires": {
"ansi-colors": "^3.0.0", "ansi-colors": "^3.0.0",
"uuid": "^3.3.2" "uuid": "^3.3.2"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
} }
}, },
"webpack-merge": { "webpack-merge": {

View File

@@ -49,6 +49,7 @@
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"html-webpack-plugin": "^4.0.0", "html-webpack-plugin": "^4.0.0",
"knowdesign": "^1.3.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.24.0", "moment": "^2.24.0",
"react": "16.12.0", "react": "16.12.0",
@@ -59,9 +60,8 @@
"react-joyride": "^2.5.0", "react-joyride": "^2.5.0",
"single-spa": "5.9.3", "single-spa": "5.9.3",
"single-spa-react": "2.14.0", "single-spa-react": "2.14.0",
"webpack-bundle-analyzer": "^4.5.0", "tree-changes": "0.9.1",
"knowdesign": "1.3.7", "webpack-bundle-analyzer": "^4.5.0"
"tree-changes": "0.9.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.5.5", "@babel/core": "^7.5.5",

View File

@@ -261,3 +261,6 @@ export const timeFormater = function formatDuring(mss: number) {
.map((o: any) => `${o.v}${o.unit}`) .map((o: any) => `${o.v}${o.unit}`)
.join(); .join();
}; };
// 列表页Header布局前缀
export const tableHeaderPrefix = 'table-header-layout';

View File

@@ -280,3 +280,38 @@ li {
line-height: 20px; line-height: 20px;
} }
} }
// Table Header 布局样式
.table-header-layout {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
&-left,
&-right {
display: flex;
align-items: center;
}
&-left {
&-refresh{
font-size: 20px;
color: #74788d;
cursor: pointer;
}
}
&-right{
&>*{
margin-left: 8px;
}
.search-input {
width: 248px;
}
}
&-divider{
height: 20px;
top: 0
}
}

View File

@@ -21,7 +21,6 @@ export default {
[`menu.${systemKey}.cluster`]: 'Cluster', [`menu.${systemKey}.cluster`]: 'Cluster',
[`menu.${systemKey}.cluster.overview`]: 'Overview', [`menu.${systemKey}.cluster.overview`]: 'Overview',
[`menu.${systemKey}.cluster.balance`]: 'Load Rebalance',
[`menu.${systemKey}.broker`]: 'Broker', [`menu.${systemKey}.broker`]: 'Broker',
[`menu.${systemKey}.broker.dashbord`]: 'Overview', [`menu.${systemKey}.broker.dashbord`]: 'Overview',
@@ -45,7 +44,7 @@ export default {
[`menu.${systemKey}.consumer-group.group-list`]: 'GroupList', [`menu.${systemKey}.consumer-group.group-list`]: 'GroupList',
[`menu.${systemKey}.operation`]: 'Operation', [`menu.${systemKey}.operation`]: 'Operation',
[`menu.${systemKey}.operation.balance`]: 'Load Rebalance', [`menu.${systemKey}.operation.balance`]: 'Rebalance',
[`menu.${systemKey}.operation.jobs`]: 'Job', [`menu.${systemKey}.operation.jobs`]: 'Job',
[`menu.${systemKey}.acls`]: 'ACLs', [`menu.${systemKey}.acls`]: 'ACLs',

View File

@@ -1,12 +1,13 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useParams, useHistory, useLocation } from 'react-router-dom'; import { useParams, useHistory, useLocation } from 'react-router-dom';
import { ProTable, Utils, AppContainer } from 'knowdesign'; import { ProTable, Utils, AppContainer, SearchInput, IconFont } from 'knowdesign';
import API from '../../api'; import API from '../../api';
import { getControllerChangeLogListColumns, defaultPagination } from './config'; import { getControllerChangeLogListColumns, defaultPagination } from './config';
import BrokerDetail from '../BrokerDetail'; import BrokerDetail from '../BrokerDetail';
import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck'; import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck';
import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb'; import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
import './index.less'; import './index.less';
import { tableHeaderPrefix } from '@src/constants/common';
const { request } = Utils; const { request } = Utils;
const ControllerChangeLogList: React.FC = (props: any) => { const ControllerChangeLogList: React.FC = (props: any) => {
@@ -89,26 +90,35 @@ const ControllerChangeLogList: React.FC = (props: any) => {
<BrokerHealthCheck /> <BrokerHealthCheck />
</div> </div>
<div className="clustom-table-content"> <div className="clustom-table-content">
<div className={tableHeaderPrefix}>
<div className={`${tableHeaderPrefix}-left`}>
<div
className={`${tableHeaderPrefix}-left-refresh`}
onClick={() => genData({ pageNo: pagination.current, pageSize: pagination.pageSize })}
>
<IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
</div>
</div>
<div className={`${tableHeaderPrefix}-right`}>
<SearchInput
onSearch={setSearchKeywords}
attrs={{
placeholder: '请输入Broker Host',
style: { width: '248px', borderRiadus: '8px' },
maxLength: 128,
}}
/>
</div>
</div>
<ProTable <ProTable
showQueryForm={false} showQueryForm={false}
tableProps={{ tableProps={{
showHeader: true, showHeader: false,
rowKey: 'path', rowKey: 'path',
loading: loading, loading: loading,
columns: getControllerChangeLogListColumns(), columns: getControllerChangeLogListColumns(),
dataSource: data, dataSource: data,
paginationProps: { ...pagination }, paginationProps: { ...pagination },
tableHeaderSearchInput: {
// 搜索配置
submit: getSearchKeywords,
searchInputType: 'search',
searchAttr: {
placeholder: '请输入Broker Host',
style: {
width: '248px',
},
},
},
attrs: { attrs: {
onChange: onTableChange, onChange: onTableChange,
bordered: false, bordered: false,

View File

@@ -1,9 +1,9 @@
import React, { useState, useEffect, memo } from 'react'; import React, { useState, useEffect, memo } from 'react';
import { useParams, useHistory, useLocation } from 'react-router-dom'; import { useParams, useHistory, useLocation } from 'react-router-dom';
import { ProTable, Drawer, Utils, AppContainer } from 'knowdesign'; import { ProTable, Drawer, Utils, AppContainer, SearchInput, IconFont } from 'knowdesign';
import API from '../../api'; import API from '../../api';
import { getBrokerListColumns, defaultPagination } from './config'; import { getBrokerListColumns, defaultPagination } from './config';
import { dealTableRequestParams } from '../../constants/common'; import { tableHeaderPrefix } from '@src/constants/common';
import BrokerDetail from '../BrokerDetail'; import BrokerDetail from '../BrokerDetail';
import CardBar from '@src/components/CardBar'; import CardBar from '@src/components/CardBar';
import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck'; import BrokerHealthCheck from '@src/components/CardBar/BrokerHealthCheck';
@@ -33,7 +33,6 @@ const BrokerList: React.FC = (props: any) => {
if (urlParams?.clusterId === undefined) return; if (urlParams?.clusterId === undefined) return;
// filters = filters || filteredInfo; // filters = filters || filteredInfo;
setLoading(true); setLoading(true);
// const params = dealTableRequestParams({ searchKeywords, pageNo, pageSize });
const params = { const params = {
searchKeywords: searchKeywords.slice(0, 128), searchKeywords: searchKeywords.slice(0, 128),
pageNo, pageNo,
@@ -99,29 +98,36 @@ const BrokerList: React.FC = (props: any) => {
<BrokerHealthCheck /> <BrokerHealthCheck />
</div> </div>
<div className="clustom-table-content"> <div className="clustom-table-content">
<div className={tableHeaderPrefix}>
<div className={`${tableHeaderPrefix}-left`}>
<div
className={`${tableHeaderPrefix}-left-refresh`}
onClick={() => genData({ pageNo: pagination.current, pageSize: pagination.pageSize })}
>
<IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
</div>
</div>
<div className={`${tableHeaderPrefix}-right`}>
<SearchInput
onSearch={setSearchKeywords}
attrs={{
placeholder: '请输入Broker Host',
style: { width: '248px', borderRiadus: '8px' },
maxLength: 128,
}}
/>
</div>
</div>
<ProTable <ProTable
key="brokerTable" key="brokerTable"
showQueryForm={false} showQueryForm={false}
tableProps={{ tableProps={{
showHeader: true, showHeader: false,
rowKey: 'broker_list', rowKey: 'broker_list',
loading: loading, loading: loading,
columns: getBrokerListColumns(), columns: getBrokerListColumns(),
dataSource: data, dataSource: data,
paginationProps: { ...pagination }, paginationProps: { ...pagination },
tableHeaderSearchInput: {
// 搜索配置
submit: getSearchKeywords,
searchInputType: 'search',
searchAttr: {
placeholder: '请输入Broker Host',
maxLength: 128,
style: {
width: '248px',
borderRiadus: '8px',
},
},
},
attrs: { attrs: {
onChange: onTableChange, onChange: onTableChange,
scroll: { x: 'max-content', y: 'calc(100vh - 400px)' }, scroll: { x: 'max-content', y: 'calc(100vh - 400px)' },

View File

@@ -1,12 +1,8 @@
.operating-state { .operating-state {
.operation-bar { .consumers-search{
.left { display: contents;
.dcloud-form-item { .search-input-short{
margin-bottom: 0; margin-right: 8px;
}
.dcloud-form-item:first-of-type {
margin-right: 12px;
}
} }
} }
.pro-table-wrap { .pro-table-wrap {
@@ -53,6 +49,10 @@
align-items: center; align-items: center;
.d-range-time-input { .d-range-time-input {
height: 27px !important; height: 27px !important;
padding: 0 11px;
input{
line-height: 100%;
}
} }
.divider { .divider {
width: 1px; width: 1px;

View File

@@ -1,6 +1,6 @@
/* eslint-disable react/display-name */ /* eslint-disable react/display-name */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { AppContainer, Form, Input, ProTable, Select, Utils } from 'knowdesign'; import { AppContainer, Divider, Form, IconFont, Input, ProTable, Select, Utils } from 'knowdesign';
import './index.less'; import './index.less';
import Api from '@src/api/index'; import Api from '@src/api/index';
import { getOperatingStateListParams } from './interface'; import { getOperatingStateListParams } from './interface';
@@ -8,7 +8,7 @@ import { useParams } from 'react-router-dom';
import ConsumerGroupDetail from './ConsumerGroupDetail'; import ConsumerGroupDetail from './ConsumerGroupDetail';
import ConsumerGroupHealthCheck from '@src/components/CardBar/ConsumerGroupHealthCheck'; import ConsumerGroupHealthCheck from '@src/components/CardBar/ConsumerGroupHealthCheck';
import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb'; import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
import { hashDataParse } from '@src/constants/common'; import { hashDataParse, tableHeaderPrefix } from '@src/constants/common';
const { Option } = Select; const { Option } = Select;
@@ -181,17 +181,13 @@ const AutoPage = (props: any) => {
<div className={`operating-state ${scene !== 'topicDetail' && 'clustom-table-content'}`}> <div className={`operating-state ${scene !== 'topicDetail' && 'clustom-table-content'}`}>
{/* <CardBar cardColumns={data}></CardBar> */} {/* <CardBar cardColumns={data}></CardBar> */}
{scene !== 'topicDetail' && ( {scene !== 'topicDetail' && (
<div className="operation-bar"> <div className={tableHeaderPrefix}>
<div className="left"> <div className={`${tableHeaderPrefix}-left`}>
{/* <Radio.Group <div className={`${tableHeaderPrefix}-left-refresh`} onClick={() => searchFn()}>
options={showModes} <IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
optionType="button" </div>
onChange={(e) => { <Divider type="vertical" className={`${tableHeaderPrefix}-divider`} />
setShowMode(e.target.value); <div className="consumers-search">
}}
value={showMode}
/> */}
<Form.Item label="">
<Input <Input
className="search-input-short" className="search-input-short"
placeholder="请输入Consumer Group" placeholder="请输入Consumer Group"
@@ -201,8 +197,6 @@ const AutoPage = (props: any) => {
}} }}
onPressEnter={searchFn} onPressEnter={searchFn}
/> />
</Form.Item>
<Form.Item label="">
<Input <Input
className="search-input-short" className="search-input-short"
placeholder="请输入Topic name" placeholder="请输入Topic name"
@@ -212,12 +206,12 @@ const AutoPage = (props: any) => {
}} }}
onPressEnter={searchFn} onPressEnter={searchFn}
/> />
</Form.Item> </div>
{/* <Button type="primary" className="add-btn" onClick={searchFn}> {/* <Button type="primary" className="add-btn" onClick={searchFn}>
查询 查询
</Button> */} </Button> */}
</div> </div>
<div className="right"></div> {/* <div className="right"></div> */}
</div> </div>
)} )}
{/* <Table columns={columns} dataSource={consumerGroupList} scroll={{ x: 1500 }} /> {/* <Table columns={columns} dataSource={consumerGroupList} scroll={{ x: 1500 }} />

View File

@@ -13,8 +13,8 @@ interface PropsType {
} }
const typeObj: any = { const typeObj: any = {
1: '周期均衡', 1: '立即均衡',
2: '立即均衡', 2: '周期均衡',
}; };
const { request, post } = Utils; const { request, post } = Utils;

View File

@@ -1,6 +1,6 @@
import React, { useState, useEffect, memo } from 'react'; import React, { useState, useEffect, memo } from 'react';
import { useParams, useHistory, useLocation } from 'react-router-dom'; import { useParams, useHistory, useLocation } from 'react-router-dom';
import { ProTable, Drawer, Utils, AppContainer, Form, Select, Input, Button, message, Modal } from 'knowdesign'; import { ProTable, Drawer, Utils, AppContainer, Form, Select, Input, Button, message, Modal, IconFont, Divider } from 'knowdesign';
import API from '../../api'; import API from '../../api';
import { getJobsListColumns, defaultPagination, runningStatus, jobType } from './config'; import { getJobsListColumns, defaultPagination, runningStatus, jobType } from './config';
import JobsCheck from '@src/components/CardBar/JobsCheck'; import JobsCheck from '@src/components/CardBar/JobsCheck';
@@ -10,6 +10,7 @@ import './index.less';
import ReplicaChange from '@src/components/TopicJob/ReplicaChange'; import ReplicaChange from '@src/components/TopicJob/ReplicaChange';
import ReplicaMove from '@src/components/TopicJob/ReplicaMove'; import ReplicaMove from '@src/components/TopicJob/ReplicaMove';
import BalanceDrawer from '../LoadRebalance/BalanceDrawer'; import BalanceDrawer from '../LoadRebalance/BalanceDrawer';
import { tableHeaderPrefix } from '@src/constants/common';
const { request } = Utils; const { request } = Utils;
const JobsList: React.FC = (props: any) => { const JobsList: React.FC = (props: any) => {
@@ -171,35 +172,44 @@ const JobsList: React.FC = (props: any) => {
</div> </div>
{/* <Form form={form} layout="inline" onFinish={onFinish}> */} {/* <Form form={form} layout="inline" onFinish={onFinish}> */}
<div className="clustom-table-content"> <div className="clustom-table-content">
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '12px' }}> <div className={tableHeaderPrefix}>
<Form form={form} layout="inline" onFinish={onFinish}> <div className={`${tableHeaderPrefix}-left`}>
<Form.Item name="type"> <div
<Select options={jobType} style={{ width: '190px' }} className={'detail-table-select'} placeholder="选择任务类型" /> className={`${tableHeaderPrefix}-left-refresh`}
</Form.Item> onClick={() => genData({ pageNo: pagination.current, pageSize: pagination.pageSize })}
<Form.Item name="jobTarget"> >
<Input allowClear style={{ width: '190px' }} placeholder="请输入执行任务对象" /> <IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
</Form.Item> </div>
<Form.Item name="status"> <Divider type="vertical" className={`${tableHeaderPrefix}-divider`} />
<Select <Form form={form} layout="inline" onFinish={onFinish}>
mode="multiple" <Form.Item name="type">
maxTagCount={'responsive'} <Select options={jobType} style={{ width: '190px' }} className={'detail-table-select'} placeholder="选择任务类型" />
options={runningStatus} </Form.Item>
style={{ width: '190px' }} <Form.Item name="jobTarget">
className={'detail-table-select'} <Input allowClear style={{ width: '190px' }} placeholder="请输入执行任务对象" />
placeholder="选择运行状态" </Form.Item>
showArrow <Form.Item name="status">
allowClear <Select
/> mode="multiple"
</Form.Item> maxTagCount={'responsive'}
</Form> options={runningStatus}
<div> style={{ width: '190px' }}
<Form style={{ justifyContent: 'flex-end' }} form={form} layout="inline" onFinish={onFinish}> className={'detail-table-select'}
<Form.Item style={{ marginRight: 0 }}> placeholder="选择运行状态"
<Button type="primary" ghost htmlType="submit"> showArrow
allowClear
</Button> />
</Form.Item> </Form.Item>
</Form> </Form>
<div>
<Form style={{ justifyContent: 'flex-end' }} form={form} layout="inline" onFinish={onFinish}>
<Form.Item style={{ marginRight: 0 }}>
<Button type="primary" ghost htmlType="submit">
</Button>
</Form.Item>
</Form>
</div>
</div> </div>
</div> </div>
{/* </Form> */} {/* </Form> */}

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { Select, Form, Utils, AppContainer, Input, Button, ProTable, Badge, Tag, SearchInput } from 'knowdesign'; import { Select, Form, Utils, AppContainer, Input, Button, ProTable, Badge, Tag, SearchInput, IconFont, Divider } from 'knowdesign';
import BalanceDrawer from './BalanceDrawer'; import BalanceDrawer from './BalanceDrawer';
import HistoryDrawer from './HistoryDrawer'; import HistoryDrawer from './HistoryDrawer';
import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb'; import DBreadcrumb from 'knowdesign/es/extend/d-breadcrumb';
@@ -9,6 +9,7 @@ import './index.less';
import LoadRebalanceCardBar from '@src/components/CardBar/LoadRebalanceCardBar'; import LoadRebalanceCardBar from '@src/components/CardBar/LoadRebalanceCardBar';
import { BalanceFilter } from './BalanceFilter'; import { BalanceFilter } from './BalanceFilter';
import { ClustersPermissionMap } from '../CommonConfig'; import { ClustersPermissionMap } from '../CommonConfig';
import { tableHeaderPrefix } from '@src/constants/common';
const Balance_Status_OPTIONS = [ const Balance_Status_OPTIONS = [
{ {
@@ -330,7 +331,7 @@ const LoadBalance: React.FC = (props: any) => {
breadcrumbs={[ breadcrumbs={[
{ label: '多集群管理', aHref: '/' }, { label: '多集群管理', aHref: '/' },
{ label: global?.clusterInfo?.name, aHref: `/cluster/${global?.clusterInfo?.id}` }, { label: global?.clusterInfo?.name, aHref: `/cluster/${global?.clusterInfo?.id}` },
{ label: 'Load Rebalance', aHref: `` }, { label: 'Rebalance', aHref: `` },
]} ]}
/> />
</div> </div>
@@ -339,7 +340,17 @@ const LoadBalance: React.FC = (props: any) => {
</div> </div>
<div className="load-rebalance-container"> <div className="load-rebalance-container">
<div className="balance-main clustom-table-content"> <div className="balance-main clustom-table-content">
<div className="header-con"> <div className={tableHeaderPrefix}>
<div className={`${tableHeaderPrefix}-left`}>
<div
className={`${tableHeaderPrefix}-left-refresh`}
onClick={() => getList({ searchKeywords: searchValue, stateParam: balanceList })}
>
<IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
</div>
<Divider type="vertical" className={`${tableHeaderPrefix}-divider`} />
<BalanceFilter title="负载均衡列表筛选" data={[]} getNorms={getNorms} filterList={filterList} />
</div>
{/* <Form form={form} layout="inline" onFinish={resetList}> {/* <Form form={form} layout="inline" onFinish={resetList}>
<Form.Item name="status"> <Form.Item name="status">
<Select className="grid-select" placeholder="请选择状态" style={{ width: '180px' }} options={Balance_Status_OPTIONS} /> <Select className="grid-select" placeholder="请选择状态" style={{ width: '180px' }} options={Balance_Status_OPTIONS} />
@@ -354,8 +365,7 @@ const LoadBalance: React.FC = (props: any) => {
</Button> </Button>
</Form.Item> </Form.Item>
</Form> */} </Form> */}
<BalanceFilter title="负载均衡列表筛选" data={[]} getNorms={getNorms} filterList={filterList} /> <div className={`${tableHeaderPrefix}-right`}>
<div className="float-r">
<SearchInput <SearchInput
onSearch={hostSearch} onSearch={hostSearch}
attrs={{ attrs={{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 374 KiB

View File

@@ -6,9 +6,8 @@ import { regClusterName, regUsername } from '@src/constants/reg';
import { bootstrapServersErrCodes, jmxErrCodes, zkErrCodes } from './config'; import { bootstrapServersErrCodes, jmxErrCodes, zkErrCodes } from './config';
import CodeMirrorFormItem from '@src/components/CodeMirrorFormItem'; import CodeMirrorFormItem from '@src/components/CodeMirrorFormItem';
const rows = 4; const LOW_KAFKA_VERSION = '2.8.0';
const lowKafkaVersion = '2.8.0'; const CLIENT_PROPERTIES_PLACEHOLDER = `用于创建Kafka客户端进行信息获取的相关配置
const clientPropertiesPlaceholder = `用于创建Kafka客户端进行信息获取的相关配置
例如开启SCRAM-SHA-256安全管控模式的集群需输入如下配置 例如开启SCRAM-SHA-256安全管控模式的集群需输入如下配置
未开启安全管控可不进行任何输入: 未开启安全管控可不进行任何输入:
{ {
@@ -26,37 +25,25 @@ const AccessClusters = (props: any): JSX.Element => {
const intl = useIntl(); const intl = useIntl();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [loading, setLoading] = React.useState(false); const [loading, setLoading] = React.useState(false);
const [confirmLoading, setConfirmLoading] = React.useState(false);
const [curClusterInfo, setCurClusterInfo] = React.useState<any>({}); const [curClusterInfo, setCurClusterInfo] = React.useState<any>({});
const [security, setSecurity] = React.useState(curClusterInfo?.security || 'None');
const [extra, setExtra] = React.useState({ const [extra, setExtra] = React.useState({
versionExtra: '', versionExtra: '',
zooKeeperExtra: '', zooKeeperExtra: '',
bootstrapExtra: '', bootstrapExtra: '',
jmxExtra: '', jmxExtra: '',
}); });
const [isLowVersion, setIsLowVersion] = React.useState<boolean>(false);
const [zookeeperErrorStatus, setZookeeperErrorStatus] = React.useState<boolean>(false);
const lastFormItemValue = React.useRef({ const lastFormItemValue = React.useRef({
bootstrap: curClusterInfo?.bootstrapServers || '', bootstrapServers: curClusterInfo?.bootstrapServers || '',
zookeeper: curClusterInfo?.zookeeper || '', zookeeper: curClusterInfo?.zookeeper || '',
clientProperties: curClusterInfo?.clientProperties || {}, clientProperties: curClusterInfo?.clientProperties || {},
}); });
const onHandleValuesChange = (value: any, allValues: any) => { const onHandleValuesChange = (changedValue: string[]) => {
Object.keys(value).forEach((key) => { Object.keys(changedValue).forEach((key) => {
switch (key) { switch (key) {
case 'security':
setSecurity(value.security);
break;
case 'zookeeper': case 'zookeeper':
setExtra({
...extra,
zooKeeperExtra: '',
bootstrapExtra: '',
jmxExtra: '',
});
break;
case 'bootstrapServers': case 'bootstrapServers':
setExtra({ setExtra({
...extra, ...extra,
@@ -78,21 +65,19 @@ const AccessClusters = (props: any): JSX.Element => {
const onCancel = () => { const onCancel = () => {
form.resetFields(); form.resetFields();
setLoading(false); setLoading(false);
setZookeeperErrorStatus(false);
setIsLowVersion(false);
setSecurity('None');
setExtra({ setExtra({
versionExtra: '', versionExtra: '',
zooKeeperExtra: '', zooKeeperExtra: '',
bootstrapExtra: '', bootstrapExtra: '',
jmxExtra: '', jmxExtra: '',
}); });
lastFormItemValue.current = { bootstrap: '', zookeeper: '', clientProperties: {} }; lastFormItemValue.current = { bootstrapServers: '', zookeeper: '', clientProperties: {} };
props.setVisible && props.setVisible(false); props.setVisible && props.setVisible(false);
}; };
const onSubmit = () => { const onSubmit = () => {
form.validateFields().then((res) => { form.validateFields().then((res) => {
setConfirmLoading(true);
let clientProperties = null; let clientProperties = null;
try { try {
clientProperties = res.clientProperties && JSON.parse(res.clientProperties); clientProperties = res.clientProperties && JSON.parse(res.clientProperties);
@@ -107,7 +92,7 @@ const AccessClusters = (props: any): JSX.Element => {
jmxProperties: { jmxProperties: {
jmxPort: res.jmxPort, jmxPort: res.jmxPort,
maxConn: res.maxConn, maxConn: res.maxConn,
openSSL: res.security === 'Password', openSSL: res.openSSL || false,
token: res.token, token: res.token,
username: res.username, username: res.username,
}, },
@@ -115,7 +100,7 @@ const AccessClusters = (props: any): JSX.Element => {
name: res.name, name: res.name,
zookeeper: res.zookeeper || '', zookeeper: res.zookeeper || '',
}; };
setLoading(true);
if (!isNaN(curClusterInfo?.id)) { if (!isNaN(curClusterInfo?.id)) {
Utils.put(api.phyCluster, { Utils.put(api.phyCluster, {
...params, ...params,
@@ -127,7 +112,7 @@ const AccessClusters = (props: any): JSX.Element => {
onCancel(); onCancel();
}) })
.finally(() => { .finally(() => {
setLoading(false); setConfirmLoading(false);
}); });
} else { } else {
Utils.post(api.phyCluster, params) Utils.post(api.phyCluster, params)
@@ -137,7 +122,7 @@ const AccessClusters = (props: any): JSX.Element => {
onCancel(); onCancel();
}) })
.finally(() => { .finally(() => {
setLoading(false); setConfirmLoading(false);
}); });
} }
}); });
@@ -154,125 +139,224 @@ const AccessClusters = (props: any): JSX.Element => {
} }
setLoading(true); setLoading(true);
setIsLowVersion(false);
setZookeeperErrorStatus(false);
return Utils.post(api.kafkaValidator, { return Utils.post(api.kafkaValidator, {
bootstrapServers: bootstrapServers || '', bootstrapServers: bootstrapServers || '',
zookeeper: zookeeper || '', zookeeper: zookeeper || '',
clientProperties, clientProperties,
}) })
.then((res: any) => { .then(
form.setFieldsValue({ (res: {
jmxPort: res.jmxPort, errList: { code: number; message: string; data: any }[];
}); jmxPort: number | null;
kafkaVersion: string | null;
zookeeper: string | null;
}) => {
const changedValue: { jmxPort?: number; kafkaVersion?: string; zookeeper: string } = {
zookeeper: zookeeper || res.zookeeper,
};
if (res.kafkaVersion && props.kafkaVersion.includes(res.kafkaVersion)) {
changedValue.kafkaVersion = res.kafkaVersion;
}
if (res.jmxPort) {
changedValue.jmxPort = res.jmxPort;
}
form.setFieldsValue(changedValue);
if (props.kafkaVersion.indexOf(res.kafkaVersion) > -1) { const extraMsg = {
form.setFieldsValue({ ...extra,
kafkaVersion: res.kafkaVersion, // 重置默认信息为连接成功
}); bootstrapExtra: bootstrapServers ? '连接成功' : '',
} else { zooKeeperExtra: zookeeper ? '连接成功' : '',
form.setFieldsValue({ };
kafkaVersion: undefined,
const errList = res.errList || [];
// 处理错误信息
errList.forEach((item: any) => {
const { code, message } = item;
let modifyKey: 'bootstrapExtra' | 'zooKeeperExtra' | 'jmxExtra' | undefined;
if (bootstrapServersErrCodes.includes(code)) {
modifyKey = 'bootstrapExtra';
} else if (zkErrCodes.includes(code)) {
modifyKey = 'zooKeeperExtra';
} else if (jmxErrCodes.includes(code)) {
modifyKey = 'jmxExtra';
}
if (modifyKey) {
extraMsg[modifyKey] = message;
}
}); });
setExtra(extraMsg);
return res;
} }
)
form.setFieldsValue({
zookeeper: zookeeper || res.zookeeper,
});
const errList = res.errList || [];
const extraMsg = extra;
// 初始化信息为连接成功
extraMsg.bootstrapExtra = bootstrapServers ? '连接成功' : '';
extraMsg.zooKeeperExtra = zookeeper ? '连接成功' : '';
// 处理错误信息
errList.forEach((item: any) => {
const { code, message } = item;
let modifyKey: 'bootstrapExtra' | 'zooKeeperExtra' | 'jmxExtra' | undefined;
if (bootstrapServersErrCodes.includes(code)) {
modifyKey = 'bootstrapExtra';
} else if (zkErrCodes.includes(code)) {
modifyKey = 'zooKeeperExtra';
} else if (jmxErrCodes.includes(code)) {
modifyKey = 'jmxExtra';
}
if (modifyKey) {
extraMsg[modifyKey] = `连接失败。${message}`;
}
});
// 如果kafkaVersion小于最低版本则提示
const showLowVersion = !(
curClusterInfo?.zookeeper ||
!curClusterInfo?.kafkaVersion ||
curClusterInfo?.kafkaVersion >= lowKafkaVersion
);
setIsLowVersion(showLowVersion);
setExtra({
...extraMsg,
versionExtra: showLowVersion ? intl.formatMessage({ id: 'access.cluster.low.version.tip' }) : '',
});
return res;
})
.finally(() => { .finally(() => {
setLoading(false); setLoading(false);
}); });
}; };
// 更新表单状态
React.useEffect(() => { React.useEffect(() => {
const showLowVersion = !(curClusterInfo?.zookeeper || !curClusterInfo?.kafkaVersion || curClusterInfo?.kafkaVersion >= lowKafkaVersion);
lastFormItemValue.current = { lastFormItemValue.current = {
bootstrap: curClusterInfo?.bootstrapServers || '', bootstrapServers: curClusterInfo?.bootstrapServers || '',
zookeeper: curClusterInfo?.zookeeper || '', zookeeper: curClusterInfo?.zookeeper || '',
clientProperties: curClusterInfo?.clientProperties || {}, clientProperties: curClusterInfo?.clientProperties || {},
}; };
setIsLowVersion(showLowVersion);
setExtra({
...extra,
versionExtra: showLowVersion ? intl.formatMessage({ id: 'access.cluster.low.version.tip' }) : '',
});
form.setFieldsValue({ ...curClusterInfo }); form.setFieldsValue({ ...curClusterInfo });
if (curClusterInfo?.kafkaVersion) {
form.validateFields(['kafkaVersion']);
}
}, [curClusterInfo]); }, [curClusterInfo]);
// 获取集群详情数据
React.useEffect(() => { React.useEffect(() => {
if (visible) { if (visible) {
if (clusterInfo?.id) { if (clusterInfo?.id) {
setLoading(true); setLoading(true);
const resolveJmxProperties = (obj: any) => {
const res = { ...obj };
try {
const originValue = obj?.jmxProperties;
if (originValue) {
const jmxProperties = JSON.parse(originValue);
typeof jmxProperties === 'object' && jmxProperties !== null && Object.assign(res, jmxProperties);
}
} catch (err) {
console.error('jmxProperties not JSON: ', err);
}
return res;
};
Utils.request(api.getPhyClusterBasic(clusterInfo.id)) Utils.request(api.getPhyClusterBasic(clusterInfo.id))
.then((res: any) => { .then((res: any) => {
let jmxProperties = null; setCurClusterInfo(resolveJmxProperties(res));
try {
jmxProperties = JSON.parse(res?.jmxProperties);
} catch (err) {
console.error(err);
}
// 转化值对应成表单值
if (jmxProperties?.openSSL) {
jmxProperties.security = 'Password';
}
if (jmxProperties) {
res = Object.assign({}, res || {}, jmxProperties);
}
setCurClusterInfo(res);
setLoading(false);
}) })
.catch((err) => { .catch((err) => {
setCurClusterInfo(clusterInfo); setCurClusterInfo(resolveJmxProperties(clusterInfo));
})
.finally(() => {
setLoading(false); setLoading(false);
}); });
} else { } else {
setCurClusterInfo(clusterInfo); setCurClusterInfo({});
} }
} }
}, [visible, clusterInfo]); }, [visible, clusterInfo]);
const validators = {
name: async (_: any, value: string) => {
if (!value) {
return Promise.reject('集群名称不能为空');
}
if (value === curClusterInfo?.name) {
return Promise.resolve();
}
if (value?.length > 128) {
return Promise.reject('集群名称长度限制在1128字符');
}
if (!new RegExp(regClusterName).test(value)) {
return Promise.reject('集群名称支持中英文、数字、特殊字符 ! " # $ % & \' ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~');
}
return Utils.request(api.getClusterBasicExit(value))
.then((res: any) => {
const data = res || {};
return data?.exist ? Promise.reject('集群名称重复') : Promise.resolve();
})
.catch(() => Promise.reject('连接超时! 请重试或检查服务'));
},
bootstrapServers: async (_: any, value: string) => {
if (!value) {
return Promise.reject('Bootstrap Servers不能为空');
}
if (value.length > 2000) {
return Promise.reject('Bootstrap Servers长度限制在2000字符');
}
if (value && value !== lastFormItemValue.current.bootstrapServers) {
lastFormItemValue.current.bootstrapServers = value;
return connectTest().catch(() => (lastFormItemValue.current.bootstrapServers = ''));
}
return Promise.resolve('');
},
zookeeper: async (_: any, value: string) => {
if (!value) {
return Promise.resolve('');
}
if (value.length > 2000) {
return Promise.reject('Zookeeper长度限制在2000字符');
}
if (value && value !== lastFormItemValue.current.zookeeper) {
lastFormItemValue.current.zookeeper = value;
return connectTest().catch(() => (lastFormItemValue.current.zookeeper = ''));
}
return Promise.resolve('');
},
securityUserName: async (_: any, value: string) => {
if (!value) {
return Promise.reject('用户名不能为空');
}
if (!new RegExp(regUsername).test(value)) {
return Promise.reject('仅支持大小写、下划线、短划线(-');
}
if (value.length > 128) {
return Promise.reject('用户名长度限制在1128字符');
}
return Promise.resolve();
},
securityToken: async (_: any, value: string) => {
if (!value) {
return Promise.reject('密码不能为空');
}
if (!new RegExp(regUsername).test(value)) {
return Promise.reject('密码只能由大小写、下划线、短划线(-)组成');
}
if (value.length < 6 || value.length > 32) {
return Promise.reject('密码长度限制在632字符');
}
return Promise.resolve();
},
kafkaVersion: async (_: any, value: any) => {
if (!value) {
return Promise.reject('版本号不能为空');
}
// 检测版本号小于2.8.0如果没有填zookeeper信息才会提示
const zookeeper = form.getFieldValue('zookeeper');
let versionExtra = '';
if (value < LOW_KAFKA_VERSION && !zookeeper) {
versionExtra = intl.formatMessage({ id: 'access.cluster.low.version.tip' });
}
setExtra({
...extra,
versionExtra,
});
return Promise.resolve();
},
clientProperties: async (_: any, value: string) => {
try {
if (value) {
JSON.parse(value);
}
return Promise.resolve();
} catch (e) {
return Promise.reject(new Error('输入内容必须为 JSON'));
}
},
description: async (_: any, value: string) => {
if (!value) {
return Promise.resolve('');
}
if (value && value.length > 200) {
return Promise.reject('集群描述长度限制在200字符');
}
return Promise.resolve();
},
};
return ( return (
<> <>
<Drawer <Drawer
@@ -285,14 +369,14 @@ const AccessClusters = (props: any): JSX.Element => {
<Button size="small" onClick={onCancel}> <Button size="small" onClick={onCancel}>
</Button> </Button>
<Button size="small" type="primary" onClick={onSubmit}> <Button size="small" type="primary" loading={confirmLoading} onClick={onSubmit}>
</Button> </Button>
<Divider type="vertical" /> <Divider type="vertical" />
</Space> </Space>
</div> </div>
} }
title={intl.formatMessage({ id: props.title || 'access.cluster' })} title={intl.formatMessage({ id: props.title || clusterInfo?.id ? 'edit.cluster' : 'access.cluster' })}
visible={props.visible} visible={props.visible}
placement="right" placement="right"
width={480} width={480}
@@ -306,30 +390,7 @@ const AccessClusters = (props: any): JSX.Element => {
rules={[ rules={[
{ {
required: true, required: true,
validator: async (rule: any, value: string) => { validator: validators.name,
if (!value) {
return Promise.reject('集群名称不能为空');
}
if (value === curClusterInfo?.name) {
return Promise.resolve();
}
if (value?.length > 128) {
return Promise.reject('集群名称长度限制在1128字符');
}
if (!new RegExp(regClusterName).test(value)) {
return Promise.reject(
'集群名称支持中英文、数字、特殊字符 ! " # $ % & \' ( ) * + , - . / : ; < = > ? @ [ ] ^ _ ` { | } ~'
);
}
return Utils.request(api.getClusterBasicExit(value)).then((res: any) => {
const data = res || {};
if (data?.exist) {
return Promise.reject('集群名称重复');
} else {
return Promise.resolve();
}
});
},
}, },
]} ]}
> >
@@ -338,31 +399,12 @@ const AccessClusters = (props: any): JSX.Element => {
<Form.Item <Form.Item
name="bootstrapServers" name="bootstrapServers"
label="Bootstrap Servers" label="Bootstrap Servers"
extra={<span className={extra.bootstrapExtra.includes('连接成功') ? 'error-extra-info' : ''}>{extra.bootstrapExtra}</span>} extra={<span className={!extra.bootstrapExtra.includes('连接成功') ? 'error-extra-info' : ''}>{extra.bootstrapExtra}</span>}
validateTrigger={'onBlur'} validateTrigger={'onBlur'}
rules={[ rules={[
{ {
required: true, required: true,
validator: async (rule: any, value: string) => { validator: validators.bootstrapServers,
if (!value) {
return Promise.reject('Bootstrap Servers不能为空');
}
if (value.length > 2000) {
return Promise.reject('Bootstrap Servers长度限制在2000字符');
}
if (value && value !== lastFormItemValue.current.bootstrap) {
return connectTest()
.then((res: any) => {
lastFormItemValue.current.bootstrap = value;
return Promise.resolve('');
})
.catch((err) => {
return Promise.reject('连接失败');
});
}
return Promise.resolve('');
},
}, },
]} ]}
> >
@@ -374,36 +416,11 @@ const AccessClusters = (props: any): JSX.Element => {
<Form.Item <Form.Item
name="zookeeper" name="zookeeper"
label="Zookeeper" label="Zookeeper"
extra={<span className={extra.zooKeeperExtra.includes('连接成功') ? 'error-extra-info' : ''}>{extra.zooKeeperExtra}</span>} extra={<span className={!extra.zooKeeperExtra.includes('连接成功') ? 'error-extra-info' : ''}>{extra.zooKeeperExtra}</span>}
validateStatus={zookeeperErrorStatus ? 'error' : 'success'}
validateTrigger={'onBlur'} validateTrigger={'onBlur'}
rules={[ rules={[
{ {
required: false, validator: validators.zookeeper,
validator: async (rule: any, value: string) => {
if (!value) {
setZookeeperErrorStatus(false);
return Promise.resolve('');
}
if (value.length > 2000) {
return Promise.reject('Zookeeper长度限制在2000字符');
}
if (value && value !== lastFormItemValue.current.zookeeper) {
return connectTest()
.then((res: any) => {
lastFormItemValue.current.zookeeper = value;
setZookeeperErrorStatus(false);
return Promise.resolve('');
})
.catch((err) => {
setZookeeperErrorStatus(true);
return Promise.reject('连接失败');
});
}
return Promise.resolve('');
},
}, },
]} ]}
> >
@@ -412,142 +429,65 @@ const AccessClusters = (props: any): JSX.Element => {
placeholder="请输入Zookeeper地址例如192.168.0.1:2181,192.168.0.2:2181,192.168.0.2:2181/ks-kafka" placeholder="请输入Zookeeper地址例如192.168.0.1:2181,192.168.0.2:2181,192.168.0.2:2181/ks-kafka"
/> />
</Form.Item> </Form.Item>
<Form.Item <Form.Item className="metrics-form-item" label="Metrics">
className="no-item-control" <div className="horizontal-form-container">
name="Metrics" <div className="inline-items">
label="Metrics" <Form.Item name="jmxPort" label="JMX Port :" extra={extra.jmxExtra}>
rules={[ <InputNumber min={0} max={99999} style={{ width: 129 }} />
{ </Form.Item>
required: false, <Form.Item name="maxConn" label="Max Conn :">
message: '', <InputNumber addonAfter="个" min={0} max={99999} style={{ width: 124 }} />
}, </Form.Item>
]} </div>
> <Form.Item name="openSSL" label="Security :">
<></> <Radio.Group>
</Form.Item> <Radio value={false}>None</Radio>
<Form.Item <Radio value={true}>Password Authentication</Radio>
name="jmxPort" </Radio.Group>
label="JMX Port"
className="inline-item adjust-height-style"
extra={extra.jmxExtra}
rules={[
{
required: false,
message: '',
},
]}
>
<InputNumber style={{ width: 134 }} min={0} max={99999} />
</Form.Item>
<Form.Item
name="maxConn"
label="MaxConn"
className="inline-item adjust-height-style"
rules={[
{
required: false,
message: '',
},
]}
>
<InputNumber style={{ width: 134 }} min={0} max={99999} />
</Form.Item>
<Form.Item
name="security"
label="Security"
className="inline-item adjust-height-style"
rules={[
{
required: false,
message: '',
},
]}
>
<Radio.Group>
<Radio value="None">None</Radio>
<Radio value="Password">Password Authentication</Radio>
</Radio.Group>
</Form.Item>
{security === 'Password' ? (
<>
<Form.Item
className="inline-item max-width-66"
name="username"
label="User Info"
style={{ width: '58%' }}
rules={[
{
required: security === 'Password' || curClusterInfo?.security === 'Password',
validator: async (rule: any, value: string) => {
if (!value) {
return Promise.reject('用户名不能为空');
}
if (!new RegExp(regUsername).test(value)) {
return Promise.reject('仅支持大小写、下划线、短划线(-');
}
if (value.length > 128) {
return Promise.reject('用户名长度限制在1128字符');
}
return Promise.resolve();
},
},
]}
>
<Input placeholder="请输入用户名" />
</Form.Item> </Form.Item>
<Form.Item <Form.Item dependencies={['openSSL']} noStyle>
className="inline-item" {({ getFieldValue }) => {
name="token" return getFieldValue('openSSL') ? (
label="" <div className="user-info-form-items">
style={{ width: '38%', marginRight: 0 }} <Form.Item className="user-info-label" label="User Info :" required />
rules={[ <div className="inline-items">
{ <Form.Item
required: security === 'Password' || curClusterInfo?.security === 'Password', name="username"
validator: async (rule: any, value: string) => { rules={[
if (!value) { {
return Promise.reject('密码不能为空'); validator: validators.securityUserName,
} },
if (!new RegExp(regUsername).test(value)) { ]}
return Promise.reject('密码只能由大小写、下划线、短划线(-)组成'); >
} <Input placeholder="请输入用户名" />
if (value.length < 6 || value.length > 32) { </Form.Item>
return Promise.reject('密码长度限制在632字符'); <Form.Item
} className="token-form-item"
return Promise.resolve(); name="token"
}, rules={[
}, {
]} validator: validators.securityToken,
> },
<Input placeholder="请输入密码" /> ]}
>
<Input placeholder="请输入密码" />
</Form.Item>
</div>
</div>
) : null;
}}
</Form.Item> </Form.Item>
</> </div>
) : null} </Form.Item>
<Form.Item <Form.Item
name="kafkaVersion" name="kafkaVersion"
label="Version" label="Version"
dependencies={['zookeeper']}
extra={<span className="error-extra-info">{extra.versionExtra}</span>} extra={<span className="error-extra-info">{extra.versionExtra}</span>}
validateStatus={isLowVersion ? 'error' : 'success'}
rules={[ rules={[
{ {
required: true, required: true,
validator: async (rule: any, value: any) => { validator: validators.kafkaVersion,
if (!value) {
setIsLowVersion(true);
return Promise.reject('版本号不能为空');
}
// 检测版本号小于2.8.0如果没有填zookeeper信息才会提示
const zookeeper = form.getFieldValue('zookeeper');
if (value < lowKafkaVersion && !zookeeper) {
setIsLowVersion(true);
setExtra({
...extra,
versionExtra: intl.formatMessage({ id: 'access.cluster.low.version.tip' }),
});
return Promise.resolve();
}
setIsLowVersion(false);
return Promise.resolve();
},
}, },
]} ]}
> >
@@ -565,29 +505,15 @@ const AccessClusters = (props: any): JSX.Element => {
label="集群配置" label="集群配置"
rules={[ rules={[
{ {
required: false, validator: validators.clientProperties,
message: '请输入集群配置',
}, },
() => ({
validator(_, value) {
try {
if (value) {
JSON.parse(value);
}
return Promise.resolve();
} catch (e) {
return Promise.reject(new Error('输入内容必须为 JSON'));
}
},
}),
]} ]}
> >
<div> <div>
<CodeMirrorFormItem <CodeMirrorFormItem
resize resize
defaultInput={form.getFieldValue('clientProperties')} defaultInput={form.getFieldValue('clientProperties')}
placeholder={clientPropertiesPlaceholder} placeholder={CLIENT_PROPERTIES_PLACEHOLDER}
onBeforeChange={(clientProperties: string) => { onBeforeChange={(clientProperties: string) => {
form.setFieldsValue({ clientProperties }); form.setFieldsValue({ clientProperties });
form.validateFields(['clientProperties']); form.validateFields(['clientProperties']);
@@ -621,20 +547,11 @@ const AccessClusters = (props: any): JSX.Element => {
label="集群描述" label="集群描述"
rules={[ rules={[
{ {
required: false, validator: validators.description,
validator: async (rule: any, value: string) => {
if (!value) {
return Promise.resolve('');
}
if (value && value.length > 200) {
return Promise.reject('集群描述长度限制在200字符');
}
return Promise.resolve();
},
}, },
]} ]}
> >
<Input.TextArea rows={rows} /> <Input.TextArea rows={4} />
</Form.Item> </Form.Item>
</Form> </Form>
</Spin> </Spin>

View File

@@ -656,43 +656,37 @@
color: @error-color; color: @error-color;
} }
} }
.inline-item.dcloud-form-item { .horizontal-form-container {
display: -webkit-inline-box; padding-left: 16px;
margin-right: 16px; .inline-items {
display: flex;
&.adjust-height-style { justify-content: space-between;
.dcloud-form-item-label {
padding: 0;
label {
height: 36px;
}
}
.dcloud-form-item-control {
&-input {
height: 36px;
}
}
} }
.dcloud-form-item {
&.max-width-66 { flex-direction: row;
.dcloud-form-item-control { align-items: center;
max-width: 66%; &-label {
} padding: 0 12px 0 0;
} font-size: 13px;
.dcloud-form-item-label {
margin-right: 12px;
label {
font-family: @font-family; font-family: @font-family;
color: #74788d;
} }
} }
} .metrics-form-item {
margin-top: 8px;
.no-item-control { }
margin-bottom: 8px !important; .user-info-form-items {
.dcloud-form-item-control { display: flex;
display: none; align-items: flex-start;
.user-info-label {
padding-top: 4px;
}
.inline-items {
flex: 0 0 80%;
.token-form-item {
margin-left: 16px;
}
}
} }
} }
} }

View File

@@ -10,11 +10,6 @@
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.01), 0 3px 6px 3px rgba(0, 0, 0, 0.01), 0 2px 6px 0 rgba(0, 0, 0, 0.03); box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.01), 0 3px 6px 3px rgba(0, 0, 0, 0.01), 0 2px 6px 0 rgba(0, 0, 0, 0.03);
// border-radius: 12px; // border-radius: 12px;
} }
.operate-bar {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
}
} }
.acls-edit-drawer { .acls-edit-drawer {

View File

@@ -1,7 +1,8 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { Button, Form, Input, Select, Modal, message, ProTable, AppContainer, DKSBreadcrumb, Utils } from 'knowdesign'; import { Button, Form, Input, Select, Modal, message, ProTable, AppContainer, DKSBreadcrumb, Utils, IconFont, Divider } from 'knowdesign';
import ACLsCardBar from '@src/components/CardBar/ACLsCardBar'; import ACLsCardBar from '@src/components/CardBar/ACLsCardBar';
import api from '@src/api'; import api from '@src/api';
import { tableHeaderPrefix } from '@src/constants/common';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import AddACLDrawer, { import AddACLDrawer, {
ACL_OPERATION, ACL_OPERATION,
@@ -205,37 +206,45 @@ const SecurityACLs = (): JSX.Element => {
<ACLsCardBar /> <ACLsCardBar />
</div> </div>
<div className="security-acls-page-list clustom-table-content"> <div className="security-acls-page-list clustom-table-content">
<div className="operate-bar"> <div className={tableHeaderPrefix}>
<Form form={form} layout="inline" onFinish={() => getACLs({ page: 1 })}> <div className={`${tableHeaderPrefix}-left`}>
<Form.Item name="kafkaUser"> <div className={`${tableHeaderPrefix}-left-refresh`} onClick={() => getACLs()}>
<Input placeholder="请输入 Principal" /> <IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
</Form.Item> </div>
<Form.Item name="resourceType"> <Divider type="vertical" className={`${tableHeaderPrefix}-divider`} />
<Select <Form form={form} layout="inline" onFinish={() => getACLs({ page: 1 })}>
placeholder="选择 ResourceType" <Form.Item name="kafkaUser">
options={Object.keys(RESOURCE_TO_OPERATIONS_MAP).map((key) => ({ label: key, value: key }))} <Input placeholder="请输入 Principal" />
mode="multiple" </Form.Item>
maxTagCount="responsive" <Form.Item name="resourceType">
allowClear <Select
style={{ width: 200 }} placeholder="选择 ResourceType"
/> options={Object.keys(RESOURCE_TO_OPERATIONS_MAP).map((key) => ({ label: key, value: key }))}
</Form.Item> mode="multiple"
<Form.Item name="resourceName"> maxTagCount="responsive"
<Input placeholder="请输入 Resource" /> allowClear
</Form.Item> style={{ width: 200 }}
<Form.Item> />
<Button type="primary" ghost htmlType="submit"> </Form.Item>
<Form.Item name="resourceName">
</Button> <Input placeholder="请输入 Resource" />
</Form.Item> </Form.Item>
</Form> <Form.Item>
<Button <Button type="primary" ghost htmlType="submit">
type="primary"
// icon={<PlusOutlined />} </Button>
onClick={() => editDrawerRef.current.onOpen(true, getACLs)} </Form.Item>
> </Form>
ACL </div>
</Button> <div className={`${tableHeaderPrefix}-right`}>
<Button
type="primary"
// icon={<PlusOutlined />}
onClick={() => editDrawerRef.current.onOpen(true, getACLs)}
>
ACL
</Button>
</div>
</div> </div>
<ProTable <ProTable
tableProps={{ tableProps={{

View File

@@ -8,15 +8,6 @@
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.01), 0 3px 6px 3px rgba(0, 0, 0, 0.01), 0 2px 6px 0 rgba(0, 0, 0, 0.03); box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.01), 0 3px 6px 3px rgba(0, 0, 0, 0.01), 0 2px 6px 0 rgba(0, 0, 0, 0.03);
border-top-left-radius: 12px; border-top-left-radius: 12px;
border-top-right-radius: 12px; border-top-right-radius: 12px;
.operate-bar {
display: flex;
justify-content: right;
margin-bottom: 12px;
.search-input {
width: 248px;
margin-right: 8px;
}
}
} }
} }

View File

@@ -22,6 +22,7 @@ import './index.less';
import api from '@src/api'; import api from '@src/api';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { regKafkaPassword } from '@src/constants/reg'; import { regKafkaPassword } from '@src/constants/reg';
import { tableHeaderPrefix } from '@src/constants/common';
export const randomString = (len = 32, chars = 'abcdefghijklmnopqrstuvwxyz1234567890'): string => { export const randomString = (len = 32, chars = 'abcdefghijklmnopqrstuvwxyz1234567890'): string => {
const maxPos = chars.length; const maxPos = chars.length;
@@ -426,34 +427,41 @@ const SecurityUsers = (): JSX.Element => {
]} ]}
/> />
<div className="security-users-page-list"> <div className="security-users-page-list">
<div className="operate-bar"> <div className={tableHeaderPrefix}>
<Input <div className={`${tableHeaderPrefix}-left`}>
className="search-input" <div className={`${tableHeaderPrefix}-left-refresh`} onClick={() => getKafkaUserList()}>
suffix={ <IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
<IconFont </div>
type="icon-fangdajing" </div>
onClick={(_) => { <div className={`${tableHeaderPrefix}-right`}>
setSearchKeywords(searchKeywordsInput); <Input
}} className="search-input"
style={{ fontSize: '16px' }} suffix={
/> <IconFont
} type="icon-fangdajing"
placeholder="请输入 Kafka User" onClick={(_) => {
value={searchKeywordsInput} setSearchKeywords(searchKeywordsInput);
onPressEnter={(_) => { }}
setSearchKeywords(searchKeywordsInput); style={{ fontSize: '16px' }}
}} />
onChange={(e) => { }
setSearchKeywordsInput(e.target.value); placeholder="请输入 Kafka User"
}} value={searchKeywordsInput}
/> onPressEnter={(_) => {
<Button setSearchKeywords(searchKeywordsInput);
type="primary" }}
// icon={<PlusOutlined />} onChange={(e) => {
onClick={() => editDrawerRef.current.onOpen(true, UsersOperate.Add, getKafkaUserList)} setSearchKeywordsInput(e.target.value);
> }}
KafkaUser />
</Button> <Button
type="primary"
// icon={<PlusOutlined />}
onClick={() => editDrawerRef.current.onOpen(true, UsersOperate.Add, getKafkaUserList)}
>
KafkaUser
</Button>
</div>
</div> </div>
<ProTable <ProTable

View File

@@ -1,53 +1,20 @@
.cluster-container-border { .cluster-detail-container-border {
background: #ffffff; background: #fff;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.01), 0 3px 6px 3px rgba(0, 0, 0, 0.01), 0 2px 6px 0 rgba(0, 0, 0, 0.03); box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.01), 0 3px 6px 3px rgba(0, 0, 0, 0.01), 0 2px 6px 0 rgba(0, 0, 0, 0.03);
border-radius: 12px; border-radius: 12px;
} }
.cluster-detail-container { .cluster-detail-container {
width: 100%;
&-header {
display: flex;
align-items: center;
height: 36px;
.refresh-icon-box {
display: flex;
justify-content: center;
align-items: center;
width: 22px;
height: 22px;
border-radius: 50%;
cursor: pointer;
.refresh-icon {
font-size: 14px;
color: #74788d;
}
&:hover {
background: #21252904;
.refresh-icon {
color: #495057;
}
}
}
}
&-main { &-main {
.header-chart-container { .header-chart-container {
width: 100%;
height: 244px; height: 244px;
margin-bottom: 12px; margin-bottom: 12px;
.cluster-container-border(); .cluster-detail-container-border();
.dcloud-spin.dcloud-spin-spinning { .dcloud-spin.dcloud-spin-spinning {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 100%; height: 100%;
height: 244px;
} }
} }
@@ -61,7 +28,7 @@
flex: 1; flex: 1;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
.cluster-container-border(); .cluster-detail-container-border();
> div { > div {
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -73,65 +40,64 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.chart-box {
position: relative;
width: 100%;
height: 244px;
background: #f8f9fa;
border-radius: 8px;
.expand-icon-box {
position: absolute;
z-index: 1000;
top: 14px;
right: 16px;
width: 24px;
height: 24px;
cursor: pointer;
font-size: 16px;
text-align: center;
border-radius: 50%;
transition: background-color 0.3s ease;
.expand-icon {
color: #adb5bc;
line-height: 24px;
}
&:hover {
background: rgba(33, 37, 41, 0.06);
.expand-icon {
color: #74788d;
}
}
}
}
} }
.config-change-records-container { .config-change-records-container {
width: 240px; width: 240px;
height: 100%; height: 100%;
margin-left: 12px; margin-left: 12px;
.cluster-container-border(); .cluster-detail-container-border();
} }
} }
} }
}
.chart-box-title { .cluster-detail-chart-box {
padding: 18px 0 0 20px; position: relative;
font-family: @font-family-bold; width: 100%;
line-height: 16px; height: 244px;
.name { background: #f8f9fa;
font-size: 14px; border-radius: 8px;
color: #212529;
.expand-icon-box {
position: absolute;
z-index: 1000;
top: 14px;
right: 16px;
width: 24px;
height: 24px;
cursor: pointer;
font-size: 16px;
text-align: center;
border-radius: 50%;
transition: background-color 0.3s ease;
.expand-icon {
color: #adb5bc;
line-height: 24px;
} }
.unit {
font-size: 12px; &:hover {
color: #495057; background: rgba(33, 37, 41, 0.06);
} .expand-icon {
> span { color: #74788d;
cursor: pointer; }
} }
} }
} }
.cluster-detail-chart-box-title {
padding: 18px 0 0 20px;
font-family: @font-family-bold;
line-height: 16px;
.name {
font-size: 14px;
color: #212529;
}
.unit {
font-size: 12px;
color: #495057;
}
> span {
cursor: pointer;
}
}

View File

@@ -15,6 +15,7 @@ import { getDataNumberUnit, getUnit } from '@src/constants/chartConfig';
import SingleChartHeader, { KsHeaderOptions } from '@src/components/SingleChartHeader'; import SingleChartHeader, { KsHeaderOptions } from '@src/components/SingleChartHeader';
import { MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL } from '@src/constants/common'; import { MAX_TIME_RANGE_WITH_SMALL_POINT_INTERVAL } from '@src/constants/common';
import RenderEmpty from '@src/components/RenderEmpty'; import RenderEmpty from '@src/components/RenderEmpty';
import DragGroup from '@src/components/DragGroup';
type ChartFilterOptions = Omit<KsHeaderOptions, 'gridNum'>; type ChartFilterOptions = Omit<KsHeaderOptions, 'gridNum'>;
interface MetricInfo { interface MetricInfo {
@@ -279,7 +280,7 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
}, []); }, []);
return ( return (
<div className="cluster-detail-container"> <div className="chart-panel cluster-detail-container">
<SingleChartHeader <SingleChartHeader
onChange={ksHeaderChange} onChange={ksHeaderChange}
hideNodeScope={true} hideNodeScope={true}
@@ -306,7 +307,7 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
<Spin spinning={defaultChartLoading}> <Spin spinning={defaultChartLoading}>
{messagesInMetricData.data && ( {messagesInMetricData.data && (
<> <>
<div className="chart-box-title"> <div className="cluster-detail-chart-box-title">
<Tooltip <Tooltip
placement="topLeft" placement="topLeft"
title={() => { title={() => {
@@ -354,14 +355,25 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
<div className="multiple-chart-container"> <div className="multiple-chart-container">
<div className={!metricDataList.length ? 'multiple-chart-container-loading' : ''}> <div className={!metricDataList.length ? 'multiple-chart-container-loading' : ''}>
<Spin spinning={chartLoading}> <Spin spinning={chartLoading}>
<Row gutter={[16, 16]}> {metricDataList.length ? (
{metricDataList.length ? ( <div className="no-group-con">
metricDataList.map((data: any, i: number) => { <DragGroup
const { metricName, metricUnit, metricLines } = data; sortableContainerProps={{
return ( onSortStart: () => 0,
<Col key={metricName} span={12}> onSortEnd: () => 0,
<div className="chart-box"> axis: 'xy',
<div className="chart-box-title"> useDragHandle: false,
}}
gridProps={{
span: 12,
gutter: [16, 16],
}}
>
{metricDataList.map((data: any, i: number) => {
const { metricName, metricUnit, metricLines } = data;
return (
<div key={metricName} className="cluster-detail-chart-box">
<div className="cluster-detail-chart-box-title">
<Tooltip <Tooltip
placement="topLeft" placement="topLeft"
title={() => { title={() => {
@@ -379,15 +391,6 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
</span> </span>
</Tooltip> </Tooltip>
</div> </div>
<div
className="expand-icon-box"
onClick={() => {
setChartDetail(data);
setShowChartDetailModal(true);
}}
>
<IconFont type="icon-chuangkoufangda" className="expand-icon" />
</div>
<SingleChart <SingleChart
chartKey={metricName} chartKey={metricName}
showHeader={false} showHeader={false}
@@ -405,15 +408,15 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
})} })}
/> />
</div> </div>
</Col> );
); })}
}) </DragGroup>
) : chartLoading ? ( </div>
<></> ) : chartLoading ? (
) : ( <></>
<RenderEmpty message="请先选择指标或刷新" /> ) : (
)} <RenderEmpty message="请先选择指标或刷新" />
</Row> )}
</Spin> </Spin>
</div> </div>
</div> </div>
@@ -421,35 +424,6 @@ const DetailChart = (props: { children: JSX.Element }): JSX.Element => {
<div className="config-change-records-container">{props.children}</div> <div className="config-change-records-container">{props.children}</div>
</div> </div>
</div> </div>
{/* 图表详情 */}
<Modal
width={1080}
visible={showChartDetailModal}
centered={true}
footer={null}
closable={false}
onCancel={() => setShowChartDetailModal(false)}
>
<div className="chart-detail-modal-container">
<div className="expand-icon-box" onClick={() => setShowChartDetailModal(false)}>
<IconFont type="icon-chuangkousuoxiao" className="expand-icon" />
</div>
{chartDetail && (
<SingleChart
chartTypeProp="line"
wrapStyle={{
width: 'auto',
height: 462,
}}
propChartData={chartDetail.metricLines}
{...getChartConfig({
metricName: `${chartDetail.metricName}{unit|${chartDetail.metricUnit}}`,
})}
/>
)}
</div>
</Modal>
</div> </div>
); );
}; };

View File

@@ -231,9 +231,10 @@
} }
.chart-panel { .chart-panel {
flex: 1; flex: auto;
margin-left: 12px; margin-left: 12px;
margin-right: 10px; margin-right: 10px;
overflow: hidden;
} }
.change-log-panel { .change-log-panel {

View File

@@ -21,11 +21,9 @@ const SingleClusterDetail = (): JSX.Element => {
</div> </div>
<div className="cluster-detail"> <div className="cluster-detail">
<LeftSider /> <LeftSider />
<div className="chart-panel"> <ChartPanel>
<ChartPanel> <ChangeLog />
<ChangeLog /> </ChartPanel>
</ChartPanel>
</div>
</div> </div>
</div> </div>
</> </>

View File

@@ -268,6 +268,7 @@ export const getFormConfig = (topicMetaData: any, info = {} as any, partitionLis
type: FormItemType.inputNumber, type: FormItemType.inputNumber,
attrs: { attrs: {
min: 1, min: 1,
max: 1000,
}, },
invisible: !info?.needMsgNum, invisible: !info?.needMsgNum,
rules: [ rules: [

View File

@@ -152,6 +152,7 @@ export const getFormConfig = (params: any) => {
rules: [{ required: true, message: '请输入' }], rules: [{ required: true, message: '请输入' }],
attrs: { attrs: {
min: 0, min: 0,
max: 1000,
style: { width: 232 }, style: { width: 232 },
}, },
}, },
@@ -391,7 +392,7 @@ export const getTableColumns = () => {
{ {
title: 'time', title: 'time',
dataIndex: 'costTimeUnitMs', dataIndex: 'costTimeUnitMs',
width: 60, width: 100,
}, },
]; ];
}; };

View File

@@ -10,6 +10,7 @@ const defaultParams: any = {
maxRecords: 100, maxRecords: 100,
pullTimeoutUnitMs: 5000, pullTimeoutUnitMs: 5000,
// filterPartitionId: 1, // filterPartitionId: 1,
filterOffsetReset: 0,
}; };
const defaultpaPagination = { const defaultpaPagination = {
current: 1, current: 1,
@@ -29,12 +30,20 @@ const TopicMessages = (props: any) => {
const [pagination, setPagination] = useState<any>(defaultpaPagination); const [pagination, setPagination] = useState<any>(defaultpaPagination);
const [form] = Form.useForm(); const [form] = Form.useForm();
// 获取消息开始位置
const offsetResetList = [
{ label: 'latest', value: 0 },
{ label: 'earliest', value: 1 },
];
// 默认排序 // 默认排序
const defaultSorter = { const defaultSorter = {
sortField: 'timestampUnitMs', sortField: 'timestampUnitMs',
sortType: 'desc', sortType: 'desc',
}; };
const [sorter, setSorter] = useState<any>(defaultSorter);
// 请求接口获取数据 // 请求接口获取数据
const genData = async () => { const genData = async () => {
if (urlParams?.clusterId === undefined || hashData?.topicName === undefined) return; if (urlParams?.clusterId === undefined || hashData?.topicName === undefined) return;
@@ -49,7 +58,7 @@ const TopicMessages = (props: any) => {
}); });
setPartitionIdList(newPartitionIdList || []); setPartitionIdList(newPartitionIdList || []);
}); });
request(Api.getTopicMessagesList(hashData?.topicName, urlParams?.clusterId), { data: { ...params, ...defaultSorter }, method: 'POST' }) request(Api.getTopicMessagesList(hashData?.topicName, urlParams?.clusterId), { data: { ...params, ...sorter }, method: 'POST' })
.then((res: any) => { .then((res: any) => {
// setPagination({ // setPagination({
// current: res.pagination?.pageNo, // current: res.pagination?.pageNo,
@@ -87,8 +96,15 @@ const TopicMessages = (props: any) => {
history.push(`/cluster/${urlParams?.clusterId}/testing/consumer`); history.push(`/cluster/${urlParams?.clusterId}/testing/consumer`);
}; };
const onTableChange = (pagination: any, filters: any, sorter: any) => { const onTableChange = (pagination: any, filters: any, sorter: any, extra: any) => {
setPagination(pagination); setPagination(pagination);
// 只有排序事件时,触发重新请求后端数据
if (extra.action === 'sort') {
setSorter({
sortField: sorter.field || '',
sortType: sorter.order ? sorter.order.substring(0, sorter.order.indexOf('end')) : '',
});
}
// const asc = sorter?.order && sorter?.order === 'ascend' ? true : false; // const asc = sorter?.order && sorter?.order === 'ascend' ? true : false;
// const sortColumn = sorter.field && toLine(sorter.field); // const sortColumn = sorter.field && toLine(sorter.field);
// genData({ pageNo: pagination.current, pageSize: pagination.pageSize, filters, asc, sortColumn, queryTerm: searchResult, ...allParams }); // genData({ pageNo: pagination.current, pageSize: pagination.pageSize, filters, asc, sortColumn, queryTerm: searchResult, ...allParams });
@@ -96,7 +112,7 @@ const TopicMessages = (props: any) => {
useEffect(() => { useEffect(() => {
props.positionType === 'Messages' && genData(); props.positionType === 'Messages' && genData();
}, [props, params]); }, [props, params, sorter]);
return ( return (
<> <>
@@ -119,6 +135,15 @@ const TopicMessages = (props: any) => {
</div> </div>
<div className="messages-query"> <div className="messages-query">
<Form form={form} layout="inline" onFinish={onFinish}> <Form form={form} layout="inline" onFinish={onFinish}>
<Form.Item name="filterOffsetReset">
<Select
options={offsetResetList}
size="small"
style={{ width: '120px' }}
className={'detail-table-select'}
placeholder="请选择offset"
/>
</Form.Item>
<Form.Item name="filterPartitionId"> <Form.Item name="filterPartitionId">
<Select <Select
options={partitionIdList} options={partitionIdList}
@@ -147,7 +172,14 @@ const TopicMessages = (props: any) => {
style={{ margin: '12px 0 4px', padding: '7px 12px', background: '#FFF9E6' }} style={{ margin: '12px 0 4px', padding: '7px 12px', background: '#FFF9E6' }}
message={ message={
<div> <div>
Topic最近的100条messagesmessages<a onClick={jumpConsume}>Produce&Consume</a> Topic 100 messages
{process.env.BUSINESS_VERSION ? (
<span>
messages <a onClick={jumpConsume}>Produce&Consume</a>
</span>
) : (
''
)}
</div> </div>
} }
type="warning" type="warning"
@@ -158,7 +190,7 @@ const TopicMessages = (props: any) => {
showQueryForm={false} showQueryForm={false}
tableProps={{ tableProps={{
showHeader: false, showHeader: false,
rowKey: 'path', rowKey: 'offset',
loading: loading, loading: loading,
columns: getTopicMessagesColmns(), columns: getTopicMessagesColmns(),
dataSource: data, dataSource: data,
@@ -169,6 +201,7 @@ const TopicMessages = (props: any) => {
bordered: false, bordered: false,
onChange: onTableChange, onChange: onTableChange,
scroll: { x: 'max-content' }, scroll: { x: 'max-content' },
sortDirections: ['descend', 'ascend', 'default'],
}, },
}} }}
/> />

View File

@@ -85,7 +85,8 @@ export const getTopicMessagesColmns = () => {
title: 'Timestamp', title: 'Timestamp',
dataIndex: 'timestampUnitMs', dataIndex: 'timestampUnitMs',
key: 'timestampUnitMs', key: 'timestampUnitMs',
render: (t: number) => (t ? moment(t).format(timeFormat) : '-'), sorter: true,
render: (t: number) => (t ? moment(t).format(timeFormat) + '.' + moment(t).millisecond() : '-'),
}, },
{ {
title: 'Key', title: 'Key',

View File

@@ -218,10 +218,10 @@ export default (props: any) => {
</Form.Item> </Form.Item>
<div className="create-topic-flex-layout"> <div className="create-topic-flex-layout">
<Form.Item name="partitionNum" label="分区数" rules={[{ required: true, message: '请输入分区数' }]}> <Form.Item name="partitionNum" label="分区数" rules={[{ required: true, message: '请输入分区数' }]}>
<InputNumber min={1} style={{ width: '100%' }} /> <InputNumber min={1} style={{ width: '100%' }} addonAfter="个" />
</Form.Item> </Form.Item>
<Form.Item name="replicaNum" label="副本数" rules={[{ required: true, message: '请输入副本数' }]}> <Form.Item name="replicaNum" label="副本数" rules={[{ required: true, message: '请输入副本数' }]}>
<InputNumber min={1} style={{ width: '100%' }} /> <InputNumber min={1} style={{ width: '100%' }} addonAfter="个" />
</Form.Item> </Form.Item>
</div> </div>
<Form.Item className="data-save-time-label" name="dataSaveTime"> <Form.Item className="data-save-time-label" name="dataSaveTime">

View File

@@ -1,48 +1,9 @@
.operation-bar { .internal-switch {
// height: 60px;
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 10px; > span {
.left { margin-left: 4px;
display: flex; color: #74788d;
align-items: center;
.divider {
margin-left: 8px;
margin-right: 8px;
width: 1px;
height: 20px;
background-color: #ced4da;
}
.internal-switch {
display: flex;
align-items: center;
> span {
margin-left: 4px;
color: #74788d;
}
}
}
.right {
display: flex;
align-items: center;
.dcloud-form-item {
margin-bottom: 0;
}
.search-input {
width: 248px;
margin-right: 8px;
}
.search-input-short {
width: 120px;
margin-right: 8px;
}
.batch-btn {
margin-right: 8px;
}
.add-btn {
width: 117px;
}
} }
} }
.operation-list { .operation-list {

View File

@@ -1,7 +1,7 @@
/* eslint-disable react/display-name */ /* eslint-disable react/display-name */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useHistory, useParams } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom';
import { AppContainer, IconFont, Input, ProTable, Select, Switch, Tooltip, Utils, Dropdown, Menu, Button } from 'knowdesign'; import { AppContainer, IconFont, Input, ProTable, Select, Switch, Tooltip, Utils, Dropdown, Menu, Button, Divider } from 'knowdesign';
import Create from './Create'; import Create from './Create';
import './index.less'; import './index.less';
import Api from '@src/api/index'; import Api from '@src/api/index';
@@ -16,6 +16,7 @@ import SmallChart from '@src/components/SmallChart';
import ReplicaMove from '@src/components/TopicJob/ReplicaMove'; import ReplicaMove from '@src/components/TopicJob/ReplicaMove';
import { formatAssignSize } from '../Jobs/config'; import { formatAssignSize } from '../Jobs/config';
import { DownOutlined } from '@ant-design/icons'; import { DownOutlined } from '@ant-design/icons';
import { tableHeaderPrefix } from '@src/constants/common';
const { Option } = Select; const { Option } = Select;
@@ -66,6 +67,7 @@ const AutoPage = (props: any) => {
// params.sortField = sortObj.sortField; // params.sortField = sortObj.sortField;
// params.sortType = sortObj.sortType || 'desc'; // params.sortType = sortObj.sortType || 'desc';
// } // }
setTopicListLoading(true);
Utils.post(Api.getTopicsList(Number(routeParams.clusterId)), params) Utils.post(Api.getTopicsList(Number(routeParams.clusterId)), params)
.then((data: any) => { .then((data: any) => {
setTopicListLoading(false); setTopicListLoading(false);
@@ -79,7 +81,6 @@ const AutoPage = (props: any) => {
}); });
}; };
useEffect(() => { useEffect(() => {
setTopicListLoading(true);
getTopicsList(); getTopicsList();
}, [sortObj, showInternalTopics, searchKeywords, pageIndex, pageSize]); }, [sortObj, showInternalTopics, searchKeywords, pageIndex, pageSize]);
@@ -285,26 +286,17 @@ const AutoPage = (props: any) => {
<TopicHealthCheck></TopicHealthCheck> <TopicHealthCheck></TopicHealthCheck>
</div> </div>
<div className="clustom-table-content"> <div className="clustom-table-content">
<div className="operation-bar"> <div className={`${tableHeaderPrefix}`}>
<div className="left"> <div className={`${tableHeaderPrefix}-left`}>
{/* 批量扩缩副本 */} {/* 批量扩缩副本 */}
<ReplicaChange drawerVisible={changeVisible} jobId={''} topics={selectedRowKeys} onClose={onclose}></ReplicaChange> <ReplicaChange drawerVisible={changeVisible} jobId={''} topics={selectedRowKeys} onClose={onclose}></ReplicaChange>
{/* 批量迁移 */} {/* 批量迁移 */}
<ReplicaMove drawerVisible={moveVisible} jobId={''} topics={selectedRowKeys} onClose={onclose}></ReplicaMove> <ReplicaMove drawerVisible={moveVisible} jobId={''} topics={selectedRowKeys} onClose={onclose}></ReplicaMove>
{/* <Select style={{ width: 140 }} placeholder="批量操作" value={selectValue} disabled={selectedRowKeys.length <= 0}>
<Option value="expandAndReduce"> <div className={`${tableHeaderPrefix}-left-refresh`} onClick={() => getTopicsList()}>
<div onClick={() => setChangeVisible(true)}>批量扩缩副本</div> <IconFont className={`${tableHeaderPrefix}-left-refresh-icon`} type="icon-shuaxin1" />
</Option> </div>
<Option value="transfer"> <Divider type="vertical" className={`${tableHeaderPrefix}-divider`} />
<div onClick={() => setMoveVisible(true)}>批量迁移</div>
</Option>
</Select> */}
{/* <Dropdown overlay={menu} disabled={selectedRowKeys.length <= 0} trigger={['click']}>
<Button icon={<DownOutlined />} type="primary" ghost disabled={selectedRowKeys.length <= 0}>
批量操作
</Button>
</Dropdown> */}
{/* <div className="divider"></div> */}
<div className="internal-switch"> <div className="internal-switch">
<Switch <Switch
size="small" size="small"
@@ -316,7 +308,7 @@ const AutoPage = (props: any) => {
<span>Topic</span> <span>Topic</span>
</div> </div>
</div> </div>
<div className="right"> <div className={`${tableHeaderPrefix}-right`}>
<Input <Input
className="search-input" className="search-input"
suffix={ suffix={

View File

@@ -1,98 +1,9 @@
const path = require('path'); const path = require('path');
require('dotenv').config({ path: path.resolve(process.cwd(), '../../.env') }); require('dotenv').config({ path: path.resolve(process.cwd(), '../../.env') });
const webpack = require('webpack');
const merge = require('webpack-merge'); const merge = require('webpack-merge');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const devMode = process.env.NODE_ENV === 'development';
const HtmlWebpackPlugin = require('html-webpack-plugin'); const commonConfig = require('./config/webpack.common');
const getWebpackCommonConfig = require('./config/d1-webpack.base'); const devConfig = require('./config/webpack.dev');
const CountPlugin = require('./config/CountComponentWebpackPlugin'); const prodConfig = require('./config/webpack.prod');
const isProd = process.env.NODE_ENV === 'production';
const jsFileName = isProd ? '[name]-[chunkhash].js' : '[name].js';
const outPath = path.resolve(__dirname, `../../../km-rest/src/main/resources/templates/layout`);
module.exports = merge(getWebpackCommonConfig(), {
mode: isProd ? 'production' : 'development',
entry: {
layout: ['./src/index.tsx'],
},
plugins: [
isProd
? new CountPlugin({
pathname: 'knowdesign',
startCount: true,
isExportExcel: false,
})
: undefined,
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
RUN_ENV: JSON.stringify(process.env.RUN_ENV),
BUSINESS_VERSION: process.env.BUSINESS_VERSION === 'true',
PUBLIC_PATH: JSON.stringify(process.env.PUBLIC_PATH),
},
}),
new HtmlWebpackPlugin({
meta: {
manifest: 'manifest.json',
},
template: './src/index.html',
favicon: path.resolve('favicon.ico'),
inject: 'body',
}),
new CopyWebpackPlugin(
[
{
from: path.resolve(__dirname, 'static'),
to: path.resolve(outPath, '../static'),
},
].concat(
isProd
? [
{
from: path.resolve(__dirname, 'favicon.ico'),
to: path.resolve(outPath, '../favicon.ico'),
},
]
: []
)
),
].filter((p) => p),
output: {
path: outPath,
publicPath: isProd ? process.env.PUBLIC_PATH + '/layout/' : '/',
filename: jsFileName,
chunkFilename: jsFileName,
library: 'layout',
libraryTarget: 'amd',
},
devServer: {
host: 'localhost',
port: 8000,
hot: true,
open: true,
openPage: 'http://localhost:8000/',
inline: true,
historyApiFallback: true,
publicPath: `http://localhost:8000/`,
headers: {
'cache-control': 'no-cache',
pragma: 'no-cache',
'Access-Control-Allow-Origin': '*',
},
proxy: {
'/ks-km/api/v3': {
changeOrigin: true,
target: 'http://localhost:8080/',
},
'/logi-security/api/v1': {
changeOrigin: true,
target: 'http://localhost:8080/',
},
},
},
resolve: { module.exports = merge(commonConfig, devMode ? devConfig : prodConfig);
alias: {
'@src': path.resolve(__dirname, 'src'),
},
},
});

View File

@@ -5,7 +5,7 @@ import com.didiglobal.logi.log.LogFactory;
import com.xiaojukeji.know.streaming.km.common.bean.entity.cluster.ClusterPhy; import com.xiaojukeji.know.streaming.km.common.bean.entity.cluster.ClusterPhy;
import com.xiaojukeji.know.streaming.km.common.exception.NotExistException; import com.xiaojukeji.know.streaming.km.common.exception.NotExistException;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminZKClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminZKClient;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import kafka.zk.KafkaZkClient; import kafka.zk.KafkaZkClient;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -7,7 +7,7 @@ import com.xiaojukeji.know.streaming.km.common.utils.BackoffUtils;
import com.xiaojukeji.know.streaming.km.core.service.change.record.KafkaChangeRecordService; import com.xiaojukeji.know.streaming.km.core.service.change.record.KafkaChangeRecordService;
import com.xiaojukeji.know.streaming.km.persistence.cache.LoadedClusterPhyCache; import com.xiaojukeji.know.streaming.km.persistence.cache.LoadedClusterPhyCache;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminZKClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminZKClient;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
public abstract class AbstractZKHandler { public abstract class AbstractZKHandler {

View File

@@ -9,7 +9,7 @@ import com.xiaojukeji.know.streaming.km.common.enums.operaterecord.OperationEnum
import com.xiaojukeji.know.streaming.km.common.utils.FutureUtil; import com.xiaojukeji.know.streaming.km.common.utils.FutureUtil;
import com.xiaojukeji.know.streaming.km.core.service.broker.BrokerService; import com.xiaojukeji.know.streaming.km.core.service.broker.BrokerService;
import com.xiaojukeji.know.streaming.km.core.service.change.record.KafkaChangeRecordService; import com.xiaojukeji.know.streaming.km.core.service.change.record.KafkaChangeRecordService;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import kafka.zk.BrokerIdsZNode; import kafka.zk.BrokerIdsZNode;
import kafka.zookeeper.ZNodeChildChangeHandler; import kafka.zookeeper.ZNodeChildChangeHandler;

View File

@@ -8,11 +8,11 @@ import com.xiaojukeji.know.streaming.km.common.enums.KafkaConfigTypeEnum;
import com.xiaojukeji.know.streaming.km.common.enums.operaterecord.OperationEnum; import com.xiaojukeji.know.streaming.km.common.enums.operaterecord.OperationEnum;
import com.xiaojukeji.know.streaming.km.common.utils.FutureUtil; import com.xiaojukeji.know.streaming.km.common.utils.FutureUtil;
import com.xiaojukeji.know.streaming.km.common.utils.Tuple; import com.xiaojukeji.know.streaming.km.common.utils.Tuple;
import com.xiaojukeji.know.streaming.km.common.zookeeper.znode.config.ConfigChangeNotificationBaseData; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.znode.config.ConfigChangeNotificationBaseData;
import com.xiaojukeji.know.streaming.km.common.zookeeper.znode.config.ConfigChangeNotificationDataV1; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.znode.config.ConfigChangeNotificationDataV1;
import com.xiaojukeji.know.streaming.km.common.zookeeper.znode.config.ConfigChangeNotificationDataV2; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.znode.config.ConfigChangeNotificationDataV2;
import com.xiaojukeji.know.streaming.km.core.service.change.record.KafkaChangeRecordService; import com.xiaojukeji.know.streaming.km.core.service.change.record.KafkaChangeRecordService;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import kafka.zk.ConfigEntityChangeNotificationZNode; import kafka.zk.ConfigEntityChangeNotificationZNode;
import kafka.zookeeper.ZNodeChildChangeHandler; import kafka.zookeeper.ZNodeChildChangeHandler;
import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.data.Stat;

View File

@@ -11,7 +11,7 @@ import com.xiaojukeji.know.streaming.km.common.utils.BackoffUtils;
import com.xiaojukeji.know.streaming.km.common.utils.FutureUtil; import com.xiaojukeji.know.streaming.km.common.utils.FutureUtil;
import com.xiaojukeji.know.streaming.km.core.service.change.record.KafkaChangeRecordService; import com.xiaojukeji.know.streaming.km.core.service.change.record.KafkaChangeRecordService;
import com.xiaojukeji.know.streaming.km.core.service.kafkacontroller.KafkaControllerService; import com.xiaojukeji.know.streaming.km.core.service.kafkacontroller.KafkaControllerService;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import kafka.zk.ControllerZNode; import kafka.zk.ControllerZNode;
import kafka.zookeeper.ZNodeChangeHandler; import kafka.zookeeper.ZNodeChangeHandler;

View File

@@ -9,7 +9,7 @@ import com.xiaojukeji.know.streaming.km.common.enums.operaterecord.OperationEnum
import com.xiaojukeji.know.streaming.km.common.utils.FutureUtil; import com.xiaojukeji.know.streaming.km.common.utils.FutureUtil;
import com.xiaojukeji.know.streaming.km.core.service.change.record.KafkaChangeRecordService; import com.xiaojukeji.know.streaming.km.core.service.change.record.KafkaChangeRecordService;
import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService; import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import kafka.zk.TopicsZNode; import kafka.zk.TopicsZNode;
import kafka.zookeeper.ZNodeChildChangeHandler; import kafka.zookeeper.ZNodeChildChangeHandler;

View File

@@ -24,7 +24,6 @@ import com.xiaojukeji.know.streaming.km.common.exception.VCHandlerNotExistExcept
import com.xiaojukeji.know.streaming.km.common.jmx.JmxConnectorWrap; import com.xiaojukeji.know.streaming.km.common.jmx.JmxConnectorWrap;
import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil; import com.xiaojukeji.know.streaming.km.common.utils.ConvertUtil;
import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils; import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils;
import com.xiaojukeji.know.streaming.km.common.zookeeper.znode.brokers.BrokerMetadata;
import com.xiaojukeji.know.streaming.km.core.service.broker.BrokerService; import com.xiaojukeji.know.streaming.km.core.service.broker.BrokerService;
import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService; import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService;
import com.xiaojukeji.know.streaming.km.core.service.version.BaseVersionControlService; import com.xiaojukeji.know.streaming.km.core.service.version.BaseVersionControlService;
@@ -32,8 +31,7 @@ import com.xiaojukeji.know.streaming.km.persistence.jmx.JmxDAO;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaJMXClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaJMXClient;
import com.xiaojukeji.know.streaming.km.persistence.mysql.broker.BrokerDAO; import com.xiaojukeji.know.streaming.km.persistence.mysql.broker.BrokerDAO;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import kafka.zk.BrokerIdZNode;
import kafka.zk.BrokerIdsZNode; import kafka.zk.BrokerIdsZNode;
import org.apache.kafka.clients.admin.*; import org.apache.kafka.clients.admin.*;
import org.apache.kafka.common.Node; import org.apache.kafka.common.Node;
@@ -310,9 +308,7 @@ public class BrokerServiceImpl extends BaseVersionControlService implements Brok
List<String> brokerIdList = kafkaZKDAO.getChildren(clusterPhy.getId(), BrokerIdsZNode.path(), false); List<String> brokerIdList = kafkaZKDAO.getChildren(clusterPhy.getId(), BrokerIdsZNode.path(), false);
for (String brokerId: brokerIdList) { for (String brokerId: brokerIdList) {
BrokerMetadata metadata = kafkaZKDAO.getData(clusterPhy.getId(), BrokerIdZNode.path(Integer.valueOf(brokerId)), BrokerMetadata.class); brokerList.add(kafkaZKDAO.getBrokerMetadata(clusterPhy.getId(), Integer.valueOf(brokerId)));
BrokerMetadata.parseAndUpdateBrokerMetadata(metadata);
brokerList.add(Broker.buildFrom(clusterPhy.getId(), Integer.valueOf(brokerId), metadata));
} }
return Result.buildSuc(brokerList); return Result.buildSuc(brokerList);

View File

@@ -751,8 +751,8 @@ public class ClusterMetricServiceImpl extends BaseMetricService implements Clust
private Result<ClusterMetrics> getMetricFromKafkaByTotalTopics(Long clusterId, String metric, String topicMetric){ private Result<ClusterMetrics> getMetricFromKafkaByTotalTopics(Long clusterId, String metric, String topicMetric){
List<Topic> topics = topicService.listTopicsFromCacheFirst(clusterId); List<Topic> topics = topicService.listTopicsFromCacheFirst(clusterId);
float metricsSum = 0f; float sumMetricValue = 0f;
for(Topic topic : topics){ for(Topic topic : topics) {
Result<List<TopicMetrics>> ret = topicMetricService.collectTopicMetricsFromKafkaWithCacheFirst( Result<List<TopicMetrics>> ret = topicMetricService.collectTopicMetricsFromKafkaWithCacheFirst(
clusterId, clusterId,
topic.getTopicName(), topic.getTopicName(),
@@ -763,14 +763,15 @@ public class ClusterMetricServiceImpl extends BaseMetricService implements Clust
continue; continue;
} }
List<TopicMetrics> topicMetrics = ret.getData(); for (TopicMetrics metrics : ret.getData()) {
for (TopicMetrics metrics : topicMetrics) { if(metrics.isBBrokerAgg()) {
if(metrics.isBBrokerAgg()){ Float metricValue = metrics.getMetric(topicMetric);
metricsSum += Double.valueOf(metrics.getMetrics().get(topicMetric)); sumMetricValue += (metricValue == null? 0f: metricValue);
break;
} }
} }
} }
return Result.buildSuc(initWithMetrics(clusterId, metric, metricsSum)); return Result.buildSuc(initWithMetrics(clusterId, metric, sumMetricValue));
} }
} }

View File

@@ -13,8 +13,8 @@ import com.xiaojukeji.know.streaming.km.common.enums.valid.ValidateKafkaAddressE
import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils; import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils;
import com.xiaojukeji.know.streaming.km.core.service.cluster.ClusterValidateService; import com.xiaojukeji.know.streaming.km.core.service.cluster.ClusterValidateService;
import com.xiaojukeji.know.streaming.km.persistence.jmx.JmxDAO; import com.xiaojukeji.know.streaming.km.persistence.jmx.JmxDAO;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import com.xiaojukeji.know.streaming.km.persistence.zk.impl.KafkaZKDAOImpl; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.impl.KafkaZKDAOImpl;
import kafka.server.KafkaConfig; import kafka.server.KafkaConfig;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.admin.*; import org.apache.kafka.clients.admin.*;

View File

@@ -19,7 +19,7 @@ import com.xiaojukeji.know.streaming.km.core.service.broker.BrokerService;
import com.xiaojukeji.know.streaming.km.core.service.kafkacontroller.KafkaControllerService; import com.xiaojukeji.know.streaming.km.core.service.kafkacontroller.KafkaControllerService;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient;
import com.xiaojukeji.know.streaming.km.persistence.mysql.kafkacontroller.KafkaControllerDAO; import com.xiaojukeji.know.streaming.km.persistence.mysql.kafkacontroller.KafkaControllerDAO;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import org.apache.kafka.clients.admin.*; import org.apache.kafka.clients.admin.*;
import org.apache.kafka.common.Node; import org.apache.kafka.common.Node;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -21,14 +21,14 @@ import com.xiaojukeji.know.streaming.km.common.exception.NotExistException;
import com.xiaojukeji.know.streaming.km.common.exception.VCHandlerNotExistException; import com.xiaojukeji.know.streaming.km.common.exception.VCHandlerNotExistException;
import com.xiaojukeji.know.streaming.km.common.utils.CommonUtils; import com.xiaojukeji.know.streaming.km.common.utils.CommonUtils;
import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils; import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils;
import com.xiaojukeji.know.streaming.km.common.zookeeper.znode.brokers.PartitionMap; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.znode.brokers.PartitionMap;
import com.xiaojukeji.know.streaming.km.common.zookeeper.znode.brokers.PartitionState; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.znode.brokers.PartitionState;
import com.xiaojukeji.know.streaming.km.core.service.partition.PartitionService; import com.xiaojukeji.know.streaming.km.core.service.partition.PartitionService;
import com.xiaojukeji.know.streaming.km.core.service.version.BaseVersionControlService; import com.xiaojukeji.know.streaming.km.core.service.version.BaseVersionControlService;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaConsumerClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaConsumerClient;
import com.xiaojukeji.know.streaming.km.persistence.mysql.partition.PartitionDAO; import com.xiaojukeji.know.streaming.km.persistence.mysql.partition.PartitionDAO;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import kafka.zk.TopicPartitionStateZNode; import kafka.zk.TopicPartitionStateZNode;
import kafka.zk.TopicPartitionsZNode; import kafka.zk.TopicPartitionsZNode;
import kafka.zk.TopicZNode; import kafka.zk.TopicZNode;
@@ -202,10 +202,22 @@ public class PartitionServiceImpl extends BaseVersionControlService implements P
@Override @Override
public Result<Map<TopicPartition, Long>> getPartitionOffsetFromKafka(Long clusterPhyId, String topicName, OffsetSpec offsetSpec, Long timestamp) { public Result<Map<TopicPartition, Long>> getPartitionOffsetFromKafka(Long clusterPhyId, String topicName, OffsetSpec offsetSpec, Long timestamp) {
Map<TopicPartition, OffsetSpec> topicPartitionOffsets = new HashMap<>(); Map<TopicPartition, OffsetSpec> topicPartitionOffsets = new HashMap<>();
this.listPartitionByTopic(clusterPhyId, topicName)
.stream() List<Partition> partitionList = this.listPartitionByTopic(clusterPhyId, topicName);
if (partitionList == null || partitionList.isEmpty()) {
// Topic不存在
return Result.buildFromRSAndMsg(ResultStatus.NOT_EXIST, MsgConstant.getTopicNotExist(clusterPhyId, topicName));
}
partitionList.stream()
.filter(item -> !item.getLeaderBrokerId().equals(KafkaConstant.NO_LEADER))
.forEach(elem -> topicPartitionOffsets.put(new TopicPartition(topicName, elem.getPartitionId()), offsetSpec)); .forEach(elem -> topicPartitionOffsets.put(new TopicPartition(topicName, elem.getPartitionId()), offsetSpec));
if (topicPartitionOffsets.isEmpty()) {
// 所有分区no-leader
return Result.buildFromRSAndMsg(ResultStatus.OPERATION_FAILED, MsgConstant.getPartitionNoLeader(clusterPhyId, topicName));
}
try { try {
return (Result<Map<TopicPartition, Long>>) doVCHandler(clusterPhyId, PARTITION_OFFSET_GET, new PartitionOffsetParam(clusterPhyId, topicName, topicPartitionOffsets, timestamp)); return (Result<Map<TopicPartition, Long>>) doVCHandler(clusterPhyId, PARTITION_OFFSET_GET, new PartitionOffsetParam(clusterPhyId, topicName, topicPartitionOffsets, timestamp));
} catch (VCHandlerNotExistException e) { } catch (VCHandlerNotExistException e) {

View File

@@ -23,7 +23,7 @@ import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService;
import com.xiaojukeji.know.streaming.km.core.service.version.BaseVersionControlService; import com.xiaojukeji.know.streaming.km.core.service.version.BaseVersionControlService;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminZKClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminZKClient;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import kafka.controller.ReplicaAssignment; import kafka.controller.ReplicaAssignment;
import kafka.server.ConfigType; import kafka.server.ConfigType;
import kafka.zk.AdminZkClient; import kafka.zk.AdminZkClient;

View File

@@ -30,7 +30,7 @@ import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService;
import com.xiaojukeji.know.streaming.km.core.service.version.BaseVersionControlService; import com.xiaojukeji.know.streaming.km.core.service.version.BaseVersionControlService;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminZKClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminZKClient;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import kafka.server.ConfigType; import kafka.server.ConfigType;
import kafka.zk.AdminZkClient; import kafka.zk.AdminZkClient;
import kafka.zk.KafkaZkClient; import kafka.zk.KafkaZkClient;

View File

@@ -23,7 +23,7 @@ import com.xiaojukeji.know.streaming.km.common.utils.ValidateUtils;
import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService; import com.xiaojukeji.know.streaming.km.core.service.topic.TopicService;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminClient;
import com.xiaojukeji.know.streaming.km.persistence.mysql.topic.TopicDAO; import com.xiaojukeji.know.streaming.km.persistence.mysql.topic.TopicDAO;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import kafka.zk.TopicsZNode; import kafka.zk.TopicsZNode;
import org.apache.kafka.clients.admin.*; import org.apache.kafka.clients.admin.*;
import org.apache.kafka.common.TopicPartitionInfo; import org.apache.kafka.common.TopicPartitionInfo;

View File

@@ -4,13 +4,13 @@ description: knowstreaming-manager Helm chart
type: application type: application
version: 0.1.3 version: 0.1.4
maintainers: maintainers:
- email: didicloud@didiglobal.com - email: didicloud@didiglobal.com
name: didicloud name: didicloud
appVersion: "3.0.0-beta.1" appVersion: "3.0.0-beta.2"
dependencies: dependencies:
- name: knowstreaming-web - name: knowstreaming-web

View File

@@ -173,8 +173,8 @@ antiAffinityTopologyKey: "kubernetes.io/hostname"
# Hard means that by default pods will only be scheduled if there are enough nodes for them # Hard means that by default pods will only be scheduled if there are enough nodes for them
# and that they will never end up on the same node. Setting this to soft will do this "best effort" # and that they will never end up on the same node. Setting this to soft will do this "best effort"
antiAffinity: "hard" antiAffinity: ""
#antiAffinity: "hard"
# This is the node affinity settings as defined in # This is the node affinity settings as defined in
# https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity-beta-feature # https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity-beta-feature
nodeAffinity: {} nodeAffinity: {}

View File

@@ -21,7 +21,7 @@ spec:
{{- include "ksmysql.selectorLabels" . | nindent 8 }} {{- include "ksmysql.selectorLabels" . | nindent 8 }}
spec: spec:
containers: containers:
- image: knowstreaming/knowstreaming-mysql:0.1.0 - image: knowstreaming/knowstreaming-mysql:0.2.0
name: {{ .Chart.Name }} name: {{ .Chart.Name }}
env: env:
- name: MYSQL_DATABASE - name: MYSQL_DATABASE

View File

@@ -71,6 +71,7 @@ data:
driver-class-name: org.mariadb.jdbc.Driver driver-class-name: org.mariadb.jdbc.Driver
app-name: know-streaming app-name: know-streaming
resource-extend-bean-name: myResourceExtendImpl resource-extend-bean-name: myResourceExtendImpl
login-extend-bean-name: logiSecurityDefaultLoginExtendImpl
logging: logging:
config: classpath:logback-spring.xml config: classpath:logback-spring.xml
@@ -85,11 +86,16 @@ data:
queue-size: 10000 # 每个线程池队列大小 queue-size: 10000 # 每个线程池队列大小
select-suitable-enable: true # 任务是否自动选择合适的线程池,非主要,可不修改 select-suitable-enable: true # 任务是否自动选择合适的线程池,非主要,可不修改
suitable-queue-size: 1000 # 线程池理想的队列大小,非主要,可不修改 suitable-queue-size: 1000 # 线程池理想的队列大小,非主要,可不修改
task: # 任务模块的配置 task: # 任务模块的配置
heaven: # 采集任务配置 metrics: # metrics采集任务配置
thread-num: 20 # 采集任务线程池核心线程数 thread-num: 18 # metrics采集任务线程池核心线程数
queue-size: 1000 # 采集任务线程池队列大小 queue-size: 180 # metrics采集任务线程池队列大小
metadata: # metadata同步任务配置
thread-num: 27 # metadata同步任务线程池核心线程数
queue-size: 270 # metadata同步任务线程池队列大小
common: # 剩余其他任务配置
thread-num: 15 # 剩余其他任务线程池核心线程数
queue-size: 150 # 剩余其他任务线程池队列大小
client-pool: client-pool:
@@ -99,17 +105,16 @@ data:
max-total-client-num: 20 # 最大客户端数 max-total-client-num: 20 # 最大客户端数
borrow-timeout-unit-ms: 5000 # 租借超时时间,单位秒 borrow-timeout-unit-ms: 5000 # 租借超时时间,单位秒
es:
client:
{{ if .Values.elasticsearch.enabled }} {{ if .Values.elasticsearch.enabled }}
es.client.address: elasticsearch-master:9200 address: elasticsearch-master:9200
#es.client.address: {{ .Release.Name }}-elasticsearch:9200
{{- else }} {{- else }}
es.client.address: {{ .Values.elasticsearch.esClientAddress }}:{{ .Values.elasticsearch.esProt }} address: {{ .Values.elasticsearch.esClientAddress }}:{{ .Values.elasticsearch.esProt }}
{{- end }} {{- end }}
# es.client.pass: knowstreaming-manager client-cnt: 10
# 集群自动均衡相关配置 io-thread-cnt: 2
cluster-balance: max-retry-cnt: 5
ignored-topics:
time-second: 300
# 普罗米修斯指标导出相关配置 # 普罗米修斯指标导出相关配置
management: management:
@@ -158,4 +163,3 @@ data:
curl -s -o /dev/null -X PUT http://${esaddr}:${port}/ks_kafka_topic_metric${logdate} || \ curl -s -o /dev/null -X PUT http://${esaddr}:${port}/ks_kafka_topic_metric${logdate} || \
exit 2 exit 2
done done

View File

@@ -3,7 +3,7 @@ replicaCount: 2
image: image:
repository: knowstreaming/knowstreaming-manager repository: knowstreaming/knowstreaming-manager
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
tag: "0.1.0" tag: "0.2.0"
imagePullSecrets: [] imagePullSecrets: []
nameOverride: "" nameOverride: ""
@@ -73,7 +73,7 @@ knowstreaming-web:
image: image:
repository: knowstreaming/knowstreaming-ui repository: knowstreaming/knowstreaming-ui
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
tag: "0.1.0" tag: "0.2.0"
service: service:
type: NodePort type: NodePort

View File

@@ -48,7 +48,7 @@ INSERT INTO `logi_security_permission` (`id`, `permission_name`, `parent_id`, `l
-- 初始化用户 -- 初始化用户
--INSERT INTO `logi_security_user` (`id`, `user_name`, `pw`, `real_name`, `is_delete`, `app_name`) VALUES ('1', 'admin', 'V1ZkU2RHRlhOSGxOUkVsNVdETjBRVlp0Y0V0T1IwWnlaVEZ6YWxGRVJrRkpNVEU1VTJwYVUySkhlRzlSU0RBOWUwQldha28wWVd0N1d5TkFNa0FqWFgxS05sSnNiR2hBZlE9PXtAVmpKNGFre1sjQDNAI119SjZSbGxoQH0=Mv{#cdRgJ45Lqx}3IubEW87!==', '系统管理员', '0', 'know-streaming'); -- INSERT INTO `logi_security_user` (`id`, `user_name`, `pw`, `real_name`, `is_delete`, `app_name`) VALUES ('1', 'admin', 'V1ZkU2RHRlhOSGxOUkVsNVdETjBRVlp0Y0V0T1IwWnlaVEZ6YWxGRVJrRkpNVEU1VTJwYVUySkhlRzlSU0RBOWUwQldha28wWVd0N1d5TkFNa0FqWFgxS05sSnNiR2hBZlE9PXtAVmpKNGFre1sjQDNAI119SjZSbGxoQH0=Mv{#cdRgJ45Lqx}3IubEW87!==', '系统管理员', '0', 'know-streaming');
INSERT INTO `logi_security_user` (`id`, `user_name`, `pw`, `real_name`, `is_delete`, `app_name`) VALUES ('1', 'admin', 'V1ZkU2RHRlhOVGRSUmxweFUycFNhR0V6ZEdKSk1FRjRVVU5PWkdaVmJ6SlZiWGh6WVVWQ09YdEFWbXBLTkdGcmUxc2pRREpBSTExOVNqWlNiR3hvUUgwPXtAVmpKNGFre1sjQDNAI119SjZSbGxoQH0=Mv{#cdRgJ45Lqx}3IubEW87!==', '系统管理员', '0', 'know-streaming'); INSERT INTO `logi_security_user` (`id`, `user_name`, `pw`, `real_name`, `is_delete`, `app_name`) VALUES ('1', 'admin', 'V1ZkU2RHRlhOVGRSUmxweFUycFNhR0V6ZEdKSk1FRjRVVU5PWkdaVmJ6SlZiWGh6WVVWQ09YdEFWbXBLTkdGcmUxc2pRREpBSTExOVNqWlNiR3hvUUgwPXtAVmpKNGFre1sjQDNAI119SjZSbGxoQH0=Mv{#cdRgJ45Lqx}3IubEW87!==', '系统管理员', '0', 'know-streaming');
-- 初始化角色 -- 初始化角色
@@ -96,4 +96,4 @@ INSERT INTO `logi_security_user_role` (`id`, `user_id`, `role_id`, `is_delete`,
INSERT INTO `logi_security_config` INSERT INTO `logi_security_config`
(`value_group`,`value_name`,`value`,`edit`,`status`,`memo`,`is_delete`,`app_name`,`operator`) (`value_group`,`value_name`,`value`,`edit`,`status`,`memo`,`is_delete`,`app_name`,`operator`)
VALUES VALUES
('SECURITY.LOGIN','SECURITY.TRICK_USERS','[\n \"admin\"\n]',1,1,'允许跳过登录的用户',0,'know-streaming','admin'); ('SECURITY.LOGIN','SECURITY.TRICK_USERS','[\n \"admin\"\n]',1,1,'允许跳过登录的用户',0,'know-streaming','admin');

View File

@@ -191,6 +191,10 @@ public class KafkaJMXClient extends AbstractClusterLoadedChangedHandler {
lambdaQueryWrapper.eq(BrokerPO::getStatus, Constant.ALIVE); lambdaQueryWrapper.eq(BrokerPO::getStatus, Constant.ALIVE);
BrokerPO brokerPO = brokerDAO.selectOne(lambdaQueryWrapper); BrokerPO brokerPO = brokerDAO.selectOne(lambdaQueryWrapper);
if (brokerPO == null) {
return null;
}
return Broker.buildFrom(brokerPO); return Broker.buildFrom(brokerPO);
} }
} }

View File

@@ -0,0 +1,4 @@
/**
* 读取Kafka在ZK中存储的数据的包
*/
package com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper;

View File

@@ -1,4 +1,4 @@
package com.xiaojukeji.know.streaming.km.persistence.zk; package com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service;
import com.xiaojukeji.know.streaming.km.common.bean.entity.broker.Broker; import com.xiaojukeji.know.streaming.km.common.bean.entity.broker.Broker;
import com.xiaojukeji.know.streaming.km.common.bean.entity.kafkacontroller.KafkaController; import com.xiaojukeji.know.streaming.km.common.bean.entity.kafkacontroller.KafkaController;

View File

@@ -1,4 +1,4 @@
package com.xiaojukeji.know.streaming.km.persistence.zk.impl; package com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.impl;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.didiglobal.logi.log.ILog; import com.didiglobal.logi.log.ILog;
@@ -11,11 +11,11 @@ import com.xiaojukeji.know.streaming.km.common.enums.topic.TopicTypeEnum;
import com.xiaojukeji.know.streaming.km.common.exception.AdminOperateException; import com.xiaojukeji.know.streaming.km.common.exception.AdminOperateException;
import com.xiaojukeji.know.streaming.km.common.exception.NotExistException; import com.xiaojukeji.know.streaming.km.common.exception.NotExistException;
import com.xiaojukeji.know.streaming.km.common.utils.Tuple; import com.xiaojukeji.know.streaming.km.common.utils.Tuple;
import com.xiaojukeji.know.streaming.km.common.zookeeper.znode.ControllerData; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.znode.ControllerData;
import com.xiaojukeji.know.streaming.km.common.zookeeper.znode.brokers.BrokerMetadata; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.znode.brokers.BrokerMetadata;
import com.xiaojukeji.know.streaming.km.common.zookeeper.znode.brokers.PartitionMap; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.znode.brokers.PartitionMap;
import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminZKClient; import com.xiaojukeji.know.streaming.km.persistence.kafka.KafkaAdminZKClient;
import com.xiaojukeji.know.streaming.km.persistence.zk.KafkaZKDAO; import com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.service.KafkaZKDAO;
import kafka.utils.Json; import kafka.utils.Json;
import kafka.zk.*; import kafka.zk.*;
import kafka.zookeeper.AsyncResponse; import kafka.zookeeper.AsyncResponse;
@@ -46,14 +46,14 @@ public class KafkaZKDAOImpl implements KafkaZKDAO {
public Broker getBrokerMetadata(String zkAddress) throws KeeperException.NoNodeException, AdminOperateException { public Broker getBrokerMetadata(String zkAddress) throws KeeperException.NoNodeException, AdminOperateException {
ZooKeeper zooKeeper = null; ZooKeeper zooKeeper = null;
try { try {
zooKeeper = new ZooKeeper(zkAddress, 1000, watchedEvent -> logger.info(" receive event : " + watchedEvent.getType().name())); zooKeeper = new ZooKeeper(zkAddress, 3000, watchedEvent -> logger.info(" receive event : " + watchedEvent.getType().name()));
List<String> brokerIdList = this.getChildren(zooKeeper, BrokerIdsZNode.path()); List<String> brokerIdList = this.getChildren(zooKeeper, BrokerIdsZNode.path());
if (brokerIdList == null || brokerIdList.isEmpty()) { if (brokerIdList == null || brokerIdList.isEmpty()) {
return null; return null;
} }
BrokerMetadata brokerMetadata = this.getData(zooKeeper, BrokerIdZNode.path(Integer.parseInt(brokerIdList.get(0))), false, BrokerMetadata.class); BrokerMetadata brokerMetadata = this.getData(zooKeeper, BrokerIdZNode.path(Integer.parseInt(brokerIdList.get(0))), false, BrokerMetadata.class);
return Broker.buildFrom(null, Integer.valueOf(brokerIdList.get(0)), brokerMetadata); return this.convert2Broker(null, Integer.valueOf(brokerIdList.get(0)), brokerMetadata);
} catch (KeeperException.NoNodeException nne) { } catch (KeeperException.NoNodeException nne) {
logger.warn("method=getBrokerMetadata||zkAddress={}||errMsg=exception", zkAddress, nne); logger.warn("method=getBrokerMetadata||zkAddress={}||errMsg=exception", zkAddress, nne);
throw nne; throw nne;
@@ -79,7 +79,7 @@ public class KafkaZKDAOImpl implements KafkaZKDAO {
try { try {
BrokerMetadata metadata = this.getData(kafkaZkClient.currentZooKeeper(), BrokerIdZNode.path(brokerId), false, BrokerMetadata.class); BrokerMetadata metadata = this.getData(kafkaZkClient.currentZooKeeper(), BrokerIdZNode.path(brokerId), false, BrokerMetadata.class);
BrokerMetadata.parseAndUpdateBrokerMetadata(metadata); BrokerMetadata.parseAndUpdateBrokerMetadata(metadata);
return Broker.buildFrom(clusterPhyId, brokerId, metadata); return this.convert2Broker(clusterPhyId, brokerId, metadata);
} catch (KeeperException ke) { } catch (KeeperException ke) {
logger.error("method=getBrokerMetadata||clusterPhyId={}||brokerId={}||errMsg=exception", clusterPhyId, brokerId, ke); logger.error("method=getBrokerMetadata||clusterPhyId={}||brokerId={}||errMsg=exception", clusterPhyId, brokerId, ke);
throw ke; throw ke;
@@ -269,4 +269,18 @@ public class KafkaZKDAOImpl implements KafkaZKDAO {
byte[] bytes = zooKeeper.getData(path, addWatch, null); byte[] bytes = zooKeeper.getData(path, addWatch, null);
return JSON.parseObject(bytes, clazz); return JSON.parseObject(bytes, clazz);
} }
private Broker convert2Broker(Long clusterPhyId, Integer brokerId, BrokerMetadata brokerMetadata) {
Broker metadata = new Broker();
metadata.setClusterPhyId(clusterPhyId);
metadata.setBrokerId(brokerId);
metadata.setHost(brokerMetadata.getHost());
metadata.setPort(brokerMetadata.getPort());
metadata.setJmxPort(brokerMetadata.getJmxPort());
metadata.setStartTimestamp(brokerMetadata.getTimestamp());
metadata.setRack(brokerMetadata.getRack());
metadata.setStatus(1);
metadata.setEndpointMap(brokerMetadata.getEndpointMap());
return metadata;
}
} }

View File

@@ -1,4 +1,4 @@
package com.xiaojukeji.know.streaming.km.common.zookeeper.znode; package com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.znode;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

View File

@@ -1,4 +1,4 @@
package com.xiaojukeji.know.streaming.km.common.zookeeper.znode.brokers; package com.xiaojukeji.know.streaming.km.persistence.kafka.zookeeper.znode.brokers;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

Some files were not shown because too many files have changed in this diff Show More