Skip to content

Commit 16a2b7e

Browse files
committed
Merged main
2 parents 894935f + bac16cc commit 16a2b7e

File tree

22 files changed

+443
-70
lines changed

22 files changed

+443
-70
lines changed

.github/workflows/cve_checks.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ jobs:
6868
cache-to: type=local,dest=/tmp/.buildx-cache
6969

7070
- name: Run CVE checks
71-
uses: aquasecurity/trivy-action@18f2510ee396bbf400402947b394f2dd8c87dbb0 # infered from @v0.29.0
71+
uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 # infered from @v0.32.0
7272
with:
7373
image-ref: "ghcr.io/kafbat/kafka-ui:latest"
7474
format: "table"

README.md

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,23 @@ We extend our gratitude to Provectus for their past support in groundbreaking wo
4343
![Interface](https://raw.githubusercontent.com/kafbat/kafka-ui/images/overview.gif)
4444

4545
# Features
46-
* **Multi-Cluster Management** — monitor and manage all your clusters in one place
47-
* **Performance Monitoring with Metrics Dashboard** — track key Kafka metrics with a lightweight dashboard
48-
* **View Kafka Brokers** — view topic and partition assignments, controller status
49-
* **View Kafka Topics** — view partition count, replication status, and custom configuration
50-
* **View Consumer Groups** — view per-partition parked offsets, combined and per-partition lag
51-
* **Browse Messages** — browse messages with JSON, plain text, and Avro encoding
52-
* **Dynamic Topic Configuration** — create and configure new topics with dynamic configuration
53-
* **Configurable Authentication**[secure](https://ui.docs.kafbat.io/configuration/authentication) your installation with optional Github/Gitlab/Google OAuth 2.0
54-
* **Custom serialization/deserialization plugins** - [use](https://ui.docs.kafbat.io/configuration/serialization-serde) a ready-to-go serde for your data like AWS Glue or Smile, or code your own!
55-
* **Role based access control** - [manage permissions](https://ui.docs.kafbat.io/configuration/rbac-role-based-access-control) to access the UI with granular precision
56-
* **Data masking** - [obfuscate](https://ui.docs.kafbat.io/configuration/data-masking) sensitive data in topic messages
46+
47+
* **Topic Insights** – View essential topic details including partition count, replication status, and custom configurations.
48+
* **Configuration Wizard** – Set up and configure your Kafka clusters directly through the UI.
49+
* **Multi-Cluster Management** – Monitor and manage all your Kafka clusters in one unified interface.
50+
* **Metrics Dashboard** – Track key Kafka metrics in real time with a streamlined, lightweight dashboard.
51+
* **Kafka Brokers Overview** – Inspect brokers, including partition assignments and controller status.
52+
* **Consumer Group Details** – Analyze parked offsets per partition, and monitor both combined and partition-specific lag.
53+
* **Message Browser** – Explore messages in JSON, plain text, or Avro encoding formats. Live view is supported, enriched with user-defined CEL message filters.
54+
* **Dynamic Topic Management** – Create and configure new topics with flexible, real-time settings.
55+
* **Pluggable Authentication** – Secure your UI using OAuth 2.0 (GitHub, GitLab, Google), LDAP, or basic authentication.
56+
* **Cloud IAM Support** – Integrate with **GCP IAM**, **Azure IAM**, and **AWS IAM** for cloud-native identity and access management.
57+
* **Managed Kafka Service Support** – Full support for **Azure EventHub**, **Google Cloud Managed Service for Apache Kafka**, and **AWS Managed Streaming for Apache Kafka (MSK)**—both server-based and serverless.
58+
* **Custom SerDe Plugin Support** – Use built-in serializers/deserializers like AWS Glue and Smile, or create your own custom plugins.
59+
* **Role-Based Access Control**[Manage granular UI permissions](https://ui.docs.kafbat.io/configuration/rbac-role-based-access-control) with RBAC.
60+
* **Data Masking**[Obfuscate sensitive data](https://ui.docs.kafbat.io/configuration/data-masking) in topic messages to enhance privacy and compliance.
61+
* **MCP Server** - [Model Context Protocol](https://ui.docs.kafbat.io/faq/mcp) Server
62+
5763

5864
## Feature overview
5965

api/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# The tag is ignored when a sha is included but the reason to add it are:
22
# 1. Self Documentation: It is difficult to find out what the expected tag is given a sha alone
33
# 2. Helps dependabot during discovery of upgrades
4-
FROM azul/zulu-openjdk-alpine:21.0.6-jre-headless@sha256:75c5cc1ca1429513b56e9cbe3121bce86476cdec18b5b74b6842ab0af4b5a57f
4+
FROM azul/zulu-openjdk-alpine:21.0.8-jre-headless@sha256:9c7b4b7850bd4cdd78f91b369accc5b55beffa9a073b9a2bb94caa42606b9444
55

66
RUN apk add --no-cache \
77
# snappy codec

api/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ dependencies {
2525
implementation libs.spring.starter.oauth2.client
2626
implementation libs.spring.security.oauth2.resource.server
2727
implementation libs.spring.boot.actuator
28+
2829
compileOnly libs.spring.boot.devtools
2930

3031
implementation libs.spring.security.ldap
@@ -49,6 +50,7 @@ dependencies {
4950

5051
implementation libs.jackson.databind.nullable
5152
implementation libs.cel
53+
implementation libs.caffeine
5254
antlr libs.antlr
5355
implementation libs.antlr.runtime
5456

api/src/main/java/io/kafbat/ui/config/ClustersProperties.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import jakarta.validation.Valid;
77
import jakarta.validation.constraints.NotBlank;
88
import jakarta.validation.constraints.NotNull;
9+
import java.time.Duration;
910
import java.util.ArrayList;
1011
import java.util.HashMap;
1112
import java.util.HashSet;
@@ -39,6 +40,8 @@ public class ClustersProperties {
3940

4041
MetricsStorage defaultMetricsStorage = new MetricsStorage();
4142

43+
CacheProperties cache = new CacheProperties();
44+
4245
@Data
4346
public static class Cluster {
4447
@NotBlank(message = "field name for for cluster could not be blank")
@@ -205,6 +208,15 @@ public enum LogLevel {
205208
}
206209
}
207210

211+
@Data
212+
@NoArgsConstructor
213+
@AllArgsConstructor
214+
public static class CacheProperties {
215+
boolean enabled = true;
216+
Duration connectCacheExpiry = Duration.ofMinutes(1);
217+
Duration connectClusterCacheExpiry = Duration.ofHours(24);
218+
}
219+
208220
@PostConstruct
209221
public void validateAndSetDefaults() {
210222
if (clusters != null) {

api/src/main/java/io/kafbat/ui/controller/KafkaConnectController.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ public class KafkaConnectController extends AbstractController implements KafkaC
4545

4646
@Override
4747
public Mono<ResponseEntity<Flux<ConnectDTO>>> getConnects(String clusterName,
48+
Boolean withStats,
4849
ServerWebExchange exchange) {
4950

50-
Flux<ConnectDTO> availableConnects = kafkaConnectService.getConnects(getCluster(clusterName))
51-
.filterWhen(dto -> accessControlService.isConnectAccessible(dto, clusterName));
51+
Flux<ConnectDTO> availableConnects = kafkaConnectService.getConnects(
52+
getCluster(clusterName), withStats != null ? withStats : false
53+
).filterWhen(dto -> accessControlService.isConnectAccessible(dto, clusterName));
5254

5355
return Mono.just(ResponseEntity.ok(availableConnects));
5456
}

api/src/main/java/io/kafbat/ui/mapper/KafkaConnectMapper.java

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
package io.kafbat.ui.mapper;
22

3+
import io.kafbat.ui.config.ClustersProperties;
4+
import io.kafbat.ui.connect.model.ClusterInfo;
35
import io.kafbat.ui.connect.model.ConnectorStatusConnector;
46
import io.kafbat.ui.connect.model.ConnectorTask;
57
import io.kafbat.ui.connect.model.NewConnector;
8+
import io.kafbat.ui.model.ConnectDTO;
69
import io.kafbat.ui.model.ConnectorDTO;
710
import io.kafbat.ui.model.ConnectorPluginConfigValidationResponseDTO;
811
import io.kafbat.ui.model.ConnectorPluginDTO;
12+
import io.kafbat.ui.model.ConnectorStateDTO;
913
import io.kafbat.ui.model.ConnectorStatusDTO;
1014
import io.kafbat.ui.model.ConnectorTaskStatusDTO;
1115
import io.kafbat.ui.model.FullConnectorInfoDTO;
1216
import io.kafbat.ui.model.TaskDTO;
1317
import io.kafbat.ui.model.TaskStatusDTO;
14-
import io.kafbat.ui.model.connect.InternalConnectInfo;
18+
import io.kafbat.ui.model.connect.InternalConnectorInfo;
1519
import java.util.List;
20+
import java.util.Optional;
1621
import org.mapstruct.Mapper;
1722
import org.mapstruct.Mapping;
1823

@@ -38,7 +43,60 @@ ConnectorPluginConfigValidationResponseDTO fromClient(
3843
io.kafbat.ui.connect.model.ConnectorPluginConfigValidationResponse
3944
connectorPluginConfigValidationResponse);
4045

41-
default FullConnectorInfoDTO fullConnectorInfo(InternalConnectInfo connectInfo) {
46+
default ConnectDTO toKafkaConnect(
47+
ClustersProperties.ConnectCluster connect,
48+
List<InternalConnectorInfo> connectors,
49+
ClusterInfo clusterInfo,
50+
boolean withStats) {
51+
Integer connectorCount = null;
52+
Integer failedConnectors = null;
53+
Integer tasksCount = null;
54+
Integer failedTasksCount = null;
55+
56+
if (withStats) {
57+
connectorCount = connectors.size();
58+
failedConnectors = 0;
59+
tasksCount = 0;
60+
failedTasksCount = 0;
61+
62+
for (InternalConnectorInfo connector : connectors) {
63+
Optional<ConnectorDTO> internalConnector = Optional.ofNullable(connector.getConnector());
64+
65+
failedConnectors += internalConnector
66+
.map(ConnectorDTO::getStatus)
67+
.map(ConnectorStatusDTO::getState)
68+
.filter(ConnectorStateDTO.FAILED::equals)
69+
.map(s -> 1).orElse(0);
70+
71+
tasksCount += internalConnector.map(ConnectorDTO::getTasks).map(List::size).orElse(0);
72+
73+
if (connector.getTasks() != null) {
74+
failedTasksCount += (int) connector.getTasks().stream()
75+
.filter(t ->
76+
Optional.ofNullable(t)
77+
.map(TaskDTO::getStatus)
78+
.map(TaskStatusDTO::getState)
79+
.map(ConnectorTaskStatusDTO.FAILED::equals)
80+
.orElse(false)
81+
).count();
82+
}
83+
}
84+
85+
}
86+
87+
return new ConnectDTO()
88+
.address(connect.getAddress())
89+
.name(connect.getName())
90+
.connectorsCount(connectorCount)
91+
.failedConnectorsCount(failedConnectors)
92+
.tasksCount(tasksCount)
93+
.failedTasksCount(failedTasksCount)
94+
.version(clusterInfo.getVersion())
95+
.commit(clusterInfo.getCommit())
96+
.clusterId(clusterInfo.getKafkaClusterId());
97+
}
98+
99+
default FullConnectorInfoDTO fullConnectorInfo(InternalConnectorInfo connectInfo) {
42100
ConnectorDTO connector = connectInfo.getConnector();
43101
List<TaskDTO> tasks = connectInfo.getTasks();
44102
int failedTasksCount = (int) tasks.stream()

api/src/main/java/io/kafbat/ui/model/connect/InternalConnectInfo.java renamed to api/src/main/java/io/kafbat/ui/model/connect/InternalConnectorInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
@Data
1111
@Builder(toBuilder = true)
12-
public class InternalConnectInfo {
12+
public class InternalConnectorInfo {
1313
private final ConnectorDTO connector;
1414
private final Map<String, Object> config;
1515
private final List<TaskDTO> tasks;

api/src/main/java/io/kafbat/ui/serdes/builtin/ProtobufFileSerde.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ void configure(Configuration configuration) {
112112
&& configuration.defaultKeyMessageDescriptor() == null
113113
&& configuration.messageDescriptorMap().isEmpty()
114114
&& configuration.keyMessageDescriptorMap().isEmpty()) {
115-
throw new ValidationException("Neither default, not per-topic descriptors defined for " + name() + " serde");
115+
throw new ValidationException("Neither default, nor per-topic descriptors defined for " + name() + " serde");
116116
}
117117
this.defaultMessageDescriptor = configuration.defaultMessageDescriptor();
118118
this.defaultKeyMessageDescriptor = configuration.defaultKeyMessageDescriptor();

0 commit comments

Comments
 (0)