fix(kafka): support ZooKeeper broker discovery
This commit is contained in:
1 parent
34448cba9d
commit
8adf802444
18 files changed
+977
-77
No files matched your search
@@ -46,7 +46,7 @@ import { SQLITE_DATABASE_FILE_EXTENSIONS } from "@/lib/database/databaseFileDete
|
||||
import { connectionAttemptOriginalErrorMessage, connectionAttemptTimeoutMessage, connectionAttemptTimeoutMs } from "@/lib/connection/connectionAttemptTimeout";
|
||||
import { appendConnectionErrorHints, isJdbcMissingRuntimeDependencyError } from "@/lib/connection/connectionErrorHints";
|
||||
import { postgresTlsModeForForm } from "@/lib/connection/postgresTlsMode";
|
||||
import { normalizeKafkaBootstrapServers } from "@/lib/connection/kafkaBootstrapServers";
|
||||
import { buildMqKafkaConnectionExtra, mqKafkaConnectionTarget, resolveMqKafkaConnectionSource, type MqKafkaConnectionSource } from "@/lib/connection/mqKafkaConnection";
|
||||
import { assertCompleteDatabaseCategories, databaseSelectionForCategory } from "@/lib/connection/databaseCategoryOptions";
|
||||
import { normalizeRocketmqNamesrvAddr } from "@/lib/connection/rocketmqNamesrv";
|
||||
import { normalizeRabbitmqAddresses } from "@/lib/connection/rabbitmqAddresses";
|
||||
@@ -517,11 +517,13 @@ const configTab = ref<ConfigTab>("connection");
|
||||
const MQ_KAFKA_SECURITY_PROTOCOL_AUTO = "__auto";
|
||||
const mqAdminUrl = ref("http://127.0.0.1:8080");
|
||||
const mqSystemKind = ref<MqSystemKind>("pulsar");
|
||||
const mqKafkaConnectionSource = ref<MqKafkaConnectionSource>("bootstrap");
|
||||
const mqRocketmqNamesrvAddr = ref("127.0.0.1:9876");
|
||||
const mqRocketmqClusterName = ref("");
|
||||
const mqRabbitmqAddresses = ref("127.0.0.1:5672");
|
||||
const mqRabbitmqVirtualHost = ref("/");
|
||||
const mqKafkaBootstrapServers = ref("127.0.0.1:9092");
|
||||
const mqKafkaZooKeeperServers = ref("");
|
||||
const mqKafkaSecurityProtocol = ref(MQ_KAFKA_SECURITY_PROTOCOL_AUTO);
|
||||
const mqKafkaSaslMechanism = ref("PLAIN");
|
||||
const mqKafkaKerberosPrincipal = ref("");
|
||||
@@ -575,6 +577,10 @@ const mqKafkaSecurityProtocolOptions = computed(() => [
|
||||
{ value: "SASL_PLAINTEXT", label: "SASL_PLAINTEXT" },
|
||||
{ value: "SASL_SSL", label: "SASL_SSL" },
|
||||
]);
|
||||
const mqKafkaConnectionSourceOptions = computed(() => [
|
||||
{ value: "bootstrap" as const, label: t("connection.mqKafkaConnectionSourceBootstrap") },
|
||||
{ value: "zookeeper" as const, label: t("connection.mqKafkaConnectionSourceZooKeeper") },
|
||||
]);
|
||||
const mqKafkaSaslMechanismOptions = [
|
||||
{ value: "PLAIN", label: "PLAIN" },
|
||||
{ value: "SCRAM-SHA-256", label: "SCRAM-SHA-256" },
|
||||
@@ -892,7 +898,9 @@ function resetMqFields(config?: Partial<MqAdminConfig>) {
|
||||
mqSystemKind.value = systemKind;
|
||||
const storedAdminUrl = config?.adminUrl?.trim() || (config ? mqExtraString(config as Record<string, unknown>, "admin_url").trim() : "");
|
||||
mqAdminUrl.value = storedAdminUrl || (systemKind === "kafka" || systemKind === "rocketmq" || systemKind === "rabbitmq" ? "" : "http://127.0.0.1:8080");
|
||||
mqKafkaConnectionSource.value = resolveMqKafkaConnectionSource(extra);
|
||||
mqKafkaBootstrapServers.value = mqExtraString(extra, "bootstrapServers") || "127.0.0.1:9092";
|
||||
mqKafkaZooKeeperServers.value = mqExtraString(extra, "zookeeperServers");
|
||||
mqRocketmqNamesrvAddr.value = mqExtraString(extra, "namesrvAddr") || mqExtraString(extra, "namesrv_addr") || "127.0.0.1:9876";
|
||||
mqRocketmqClusterName.value = mqExtraString(extra, "clusterName") || mqExtraString(extra, "cluster_name");
|
||||
mqRabbitmqAddresses.value = mqExtraString(extra, "addresses") || "127.0.0.1:5672";
|
||||
@@ -969,7 +977,7 @@ watch(selectedType, () => {
|
||||
|
||||
watch(mqSystemKind, (kind) => {
|
||||
if (kind === "kafka") {
|
||||
if (!mqKafkaBootstrapServers.value.trim()) mqKafkaBootstrapServers.value = "127.0.0.1:9092";
|
||||
if (mqKafkaConnectionSource.value === "bootstrap" && !mqKafkaBootstrapServers.value.trim()) mqKafkaBootstrapServers.value = "127.0.0.1:9092";
|
||||
if (!isMqAuthKindAllowedForSystem(kind, mqAuthKind.value)) mqAuthKind.value = "none";
|
||||
return;
|
||||
}
|
||||
@@ -1108,9 +1116,14 @@ function buildMqTokenSigning() {
|
||||
function buildMqAdminConfig(): MqAdminConfig {
|
||||
const systemKind = mqSystemKind.value;
|
||||
if (systemKind === "kafka") {
|
||||
const bootstrapServers = normalizeKafkaBootstrapServers(mqKafkaBootstrapServers.value);
|
||||
const extra: Record<string, unknown> = { bootstrapServers };
|
||||
const securityProtocol = mqKafkaSecurityProtocol.value === MQ_KAFKA_SECURITY_PROTOCOL_AUTO ? "" : mqKafkaSecurityProtocol.value.trim();
|
||||
const configuredSecurityProtocol = mqKafkaSecurityProtocol.value === MQ_KAFKA_SECURITY_PROTOCOL_AUTO ? "" : mqKafkaSecurityProtocol.value;
|
||||
const extra: Record<string, unknown> = buildMqKafkaConnectionExtra({
|
||||
connectionSource: mqKafkaConnectionSource.value,
|
||||
bootstrapServers: mqKafkaBootstrapServers.value,
|
||||
zookeeperServers: mqKafkaZooKeeperServers.value,
|
||||
securityProtocol: configuredSecurityProtocol,
|
||||
});
|
||||
const securityProtocol = mqExtraString(extra, "securityProtocol");
|
||||
const saslMechanism = mqAuthKind.value === "kerberos" ? "GSSAPI" : mqKafkaSaslMechanism.value.trim();
|
||||
const properties: Record<string, string> = {};
|
||||
if (securityProtocol) extra.securityProtocol = securityProtocol;
|
||||
@@ -1518,18 +1531,17 @@ function applyMqAdminUrl(config: LegacyConnectionConfig, adminUrl: string) {
|
||||
config.ssl = parsed.protocol === "https:";
|
||||
}
|
||||
|
||||
function applyMqKafkaBootstrapServers(config: LegacyConnectionConfig, bootstrapServers: string, securityProtocol?: string) {
|
||||
const first = normalizeKafkaBootstrapServers(bootstrapServers).split(",")[0];
|
||||
if (!first) throw new Error(t("connection.mqBootstrapServersRequired"));
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(`kafka://${first}`);
|
||||
} catch {
|
||||
throw new Error(t("connection.mqBootstrapServersInvalid"));
|
||||
}
|
||||
config.host = parsed.hostname;
|
||||
config.port = Number(parsed.port) || 9092;
|
||||
config.ssl = securityProtocol === "SSL" || securityProtocol === "SASL_SSL";
|
||||
function applyMqKafkaConnectionTarget(config: LegacyConnectionConfig, extra: Record<string, unknown>) {
|
||||
const source = resolveMqKafkaConnectionSource(extra);
|
||||
const target = mqKafkaConnectionTarget({
|
||||
connectionSource: source,
|
||||
bootstrapServers: mqExtraString(extra, "bootstrapServers"),
|
||||
zookeeperServers: mqExtraString(extra, "zookeeperServers"),
|
||||
securityProtocol: mqExtraString(extra, "securityProtocol"),
|
||||
});
|
||||
config.host = target.host;
|
||||
config.port = target.port;
|
||||
config.ssl = target.ssl;
|
||||
}
|
||||
|
||||
function applyMqRabbitmqAddresses(config: LegacyConnectionConfig, addresses: string) {
|
||||
@@ -2548,7 +2560,7 @@ const connectionLabelTopClass = `${connectionLabelClass} mt-2`;
|
||||
const connectionLabelSmallPaddedClass = `${connectionLabelClass} pt-2 text-xs`;
|
||||
const hasRequiredConnectionTarget = computed(() => {
|
||||
if (form.value.db_type === "mq") {
|
||||
if (mqSystemKind.value === "kafka") return !!mqKafkaBootstrapServers.value.trim();
|
||||
if (mqSystemKind.value === "kafka") return mqKafkaConnectionSource.value === "zookeeper" ? !!mqKafkaZooKeeperServers.value.trim() : !!mqKafkaBootstrapServers.value.trim();
|
||||
if (mqSystemKind.value === "rocketmq") return !!mqRocketmqNamesrvAddr.value.trim();
|
||||
if (mqSystemKind.value === "rabbitmq") return !!mqRabbitmqAddresses.value.trim();
|
||||
return !!mqAdminUrl.value.trim();
|
||||
@@ -2881,7 +2893,7 @@ function connectionConfigForSubmit(id: string, generatedName = ""): ConnectionCo
|
||||
config.driver_label = MQ_DRIVER_LABELS[mqConfig.systemKind];
|
||||
if (mqConfig.systemKind === "kafka") {
|
||||
const extra = mqExtraRecord(mqConfig);
|
||||
applyMqKafkaBootstrapServers(config, mqExtraString(extra, "bootstrapServers"), mqExtraString(extra, "securityProtocol"));
|
||||
applyMqKafkaConnectionTarget(config, extra);
|
||||
} else if (mqConfig.systemKind === "rocketmq") {
|
||||
const extra = mqExtraRecord(mqConfig);
|
||||
applyMqRocketmqNamesrv(config, mqExtraString(extra, "namesrvAddr") || mqExtraString(extra, "namesrv_addr"));
|
||||
@@ -4769,9 +4781,26 @@ function openExternalUrl(url: string) {
|
||||
<template v-else-if="form.db_type === 'mq'">
|
||||
<template v-if="mqSystemKind === 'kafka'">
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelClass">{{ t("connection.mqKafkaConnectionSource") }}</Label>
|
||||
<Select v-model="mqKafkaConnectionSource">
|
||||
<SelectTrigger class="col-span-3 h-9">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="option in mqKafkaConnectionSourceOptions" :key="option.value" :value="option.value">
|
||||
{{ option.label }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div v-if="mqKafkaConnectionSource === 'bootstrap'" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelClass">{{ t("connection.mqBootstrapServers") }}</Label>
|
||||
<Input v-model="mqKafkaBootstrapServers" class="col-span-3" :placeholder="t('connection.mqBootstrapServersPlaceholder')" />
|
||||
</div>
|
||||
<div v-else class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelClass">{{ t("connection.mqKafkaZooKeeperServers") }}</Label>
|
||||
<Input v-model="mqKafkaZooKeeperServers" class="col-span-3" :placeholder="t('connection.mqKafkaZooKeeperServersPlaceholder')" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelClass">{{ t("connection.mqSecurity") }}</Label>
|
||||
<Select v-model="mqKafkaSecurityProtocol">
|
||||
|
||||
Reference in new issue
Block a user