mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-27 16:59:23 +08:00
✨ feat(rocketmq): 新增 RocketMQ 数据源连接与测试发消息支持
This commit is contained in:
@@ -33,10 +33,10 @@ describe('ConnectionModal data source registry', () => {
|
||||
expect(source).toContain('type === "elasticsearch"');
|
||||
expect(source).toContain("return '支持索引浏览、Mapping 检查、JSON DSL 和 query_string 查询';");
|
||||
expect(source).toContain(
|
||||
'type === "clickhouse" ? "default" : (type === "redis" || type === "elasticsearch" || type === "chroma" || type === "qdrant" || type === "mqtt" || type === "kafka" || type === "rabbitmq") ? "" : "root";',
|
||||
'type === "clickhouse" ? "default" : (type === "redis" || type === "elasticsearch" || type === "chroma" || type === "qdrant" || type === "rocketmq" || type === "mqtt" || type === "kafka" || type === "rabbitmq") ? "" : "root";',
|
||||
);
|
||||
expect(source).toContain(
|
||||
'placeholder={(dbType === "elasticsearch" || dbType === "chroma" || dbType === "qdrant" || dbType === "mqtt" || dbType === "kafka" || dbType === "rabbitmq") ? "未开启认证可留空" : undefined}',
|
||||
'placeholder={(dbType === "elasticsearch" || dbType === "chroma" || dbType === "qdrant" || dbType === "rocketmq" || dbType === "mqtt" || dbType === "kafka" || dbType === "rabbitmq") ? "未开启认证可留空" : undefined}',
|
||||
);
|
||||
expect(source).toContain('label="显示数据库 (留空显示全部)"');
|
||||
});
|
||||
@@ -77,6 +77,23 @@ describe('ConnectionModal data source registry', () => {
|
||||
expect(source).toContain('return "fetchSize=1024&timeZone=Asia%2FShanghai";');
|
||||
});
|
||||
|
||||
it('exposes RocketMQ in the create-connection picker with nameserver and topic defaults', () => {
|
||||
expect(source).toContain("case 'rocketmq':");
|
||||
expect(source).toContain('return 9876;');
|
||||
expect(source).toContain('rocketmq: ["rocketmq", "rmq"]');
|
||||
expect(source).toContain("key: 'rocketmq'");
|
||||
expect(source).toContain("name: 'RocketMQ'");
|
||||
expect(source).toContain('dbType === "rocketmq"');
|
||||
expect(source).toContain("return 'NameServer / Topic / Consumer Group';");
|
||||
expect(source).toContain('return "rocketmq://accessKey:secretKey@127.0.0.1:9876,127.0.0.2:9876/orders.events?topology=cluster&groupId=gonavi&namespace=prod&tag=TagA&pullBatchSize=32&startOffset=latest";');
|
||||
expect(source).toContain('return "groupId=gonavi&namespace=prod&tag=TagA&pullBatchSize=32&startOffset=latest";');
|
||||
expect(source).toContain('label="默认 Topic(可选)"');
|
||||
expect(source).toContain('label={dbType === "rocketmq" ? "Access Key" : "用户名"}');
|
||||
expect(source).toContain('label={dbType === "rocketmq" ? "Secret Key" : "密码"}');
|
||||
expect(source).toContain('emptyPlaceholder: dbType === "rocketmq" ? "未开启认证可留空" : "密码"');
|
||||
expect(source).toContain('retainedLabel: dbType === "rocketmq" ? "已保存 Secret Key" : "已保存密码"');
|
||||
});
|
||||
|
||||
it('exposes MQTT in the create-connection picker with broker and topic-filter defaults', () => {
|
||||
expect(source).toContain("case 'mqtt':");
|
||||
expect(source).toContain('return 1883;');
|
||||
|
||||
@@ -385,6 +385,7 @@ const ConnectionModal: React.FC<{
|
||||
);
|
||||
const disableLocalBackdropFilter = isMacLikePlatform();
|
||||
const mysqlTopology = Form.useWatch("mysqlTopology", form) || "single";
|
||||
const rocketmqTopology = Form.useWatch("rocketmqTopology", form) || "single";
|
||||
const mqttTopology = Form.useWatch("mqttTopology", form) || "single";
|
||||
const kafkaTopology = Form.useWatch("kafkaTopology", form) || "single";
|
||||
const mongoTopology = Form.useWatch("mongoTopology", form) || "single";
|
||||
@@ -420,6 +421,7 @@ const ConnectionModal: React.FC<{
|
||||
);
|
||||
const isOceanBaseOracle = dbType === "oceanbase" && oceanBaseProtocol === "oracle";
|
||||
const isMySQLLike = isMySQLCompatibleType(dbType) && !isOceanBaseOracle;
|
||||
const isRocketMQ = dbType === "rocketmq";
|
||||
const isMQTT = dbType === "mqtt";
|
||||
const isKafka = dbType === "kafka";
|
||||
const isRabbitMQ = dbType === "rabbitmq";
|
||||
@@ -1756,6 +1758,49 @@ const ConnectionModal: React.FC<{
|
||||
};
|
||||
}
|
||||
|
||||
if (type === "rocketmq") {
|
||||
const defaultPort = getDefaultPortByType(type);
|
||||
const parsed =
|
||||
parseMultiHostUri(trimmedUri, "rocketmq") ||
|
||||
parseMultiHostUri(trimmedUri, "rmq");
|
||||
if (!parsed) {
|
||||
return null;
|
||||
}
|
||||
if (!parsed.hosts.length || parsed.hosts.length > MAX_URI_HOSTS) {
|
||||
return null;
|
||||
}
|
||||
if (parsed.hosts.some((entry) => !isValidUriHostEntry(entry))) {
|
||||
return null;
|
||||
}
|
||||
const hostList = normalizeAddressList(parsed.hosts, defaultPort);
|
||||
if (!hostList.length) {
|
||||
return null;
|
||||
}
|
||||
const primary = parseHostPort(
|
||||
hostList[0] || `localhost:${defaultPort}`,
|
||||
defaultPort,
|
||||
);
|
||||
const topology = String(parsed.params.get("topology") || "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const timeoutValue = Number(parsed.params.get("timeout"));
|
||||
return {
|
||||
host: primary?.host || "localhost",
|
||||
port: primary?.port || defaultPort,
|
||||
user: parsed.username,
|
||||
password: parsed.password,
|
||||
database: parsed.database || "",
|
||||
rocketmqTopology:
|
||||
topology === "cluster" || hostList.length > 1 ? "cluster" : "single",
|
||||
rocketmqHosts: hostList.slice(1),
|
||||
connectionParams: serializeConnectionParams(parsed.params),
|
||||
timeout:
|
||||
Number.isFinite(timeoutValue) && timeoutValue > 0
|
||||
? Math.min(MAX_TIMEOUT_SECONDS, Math.trunc(timeoutValue))
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (type === "rabbitmq") {
|
||||
const defaultPort = getDefaultPortByType(type);
|
||||
const parsed = parseSingleHostUri(
|
||||
@@ -2044,6 +2089,9 @@ const ConnectionModal: React.FC<{
|
||||
if (dbType === "iotdb") {
|
||||
return "iotdb://root:root@127.0.0.1:6667/root.sg";
|
||||
}
|
||||
if (dbType === "rocketmq") {
|
||||
return "rocketmq://accessKey:secretKey@127.0.0.1:9876,127.0.0.2:9876/orders.events?topology=cluster&groupId=gonavi&namespace=prod&tag=TagA&pullBatchSize=32&startOffset=latest";
|
||||
}
|
||||
if (dbType === "mqtt") {
|
||||
return "mqtt://user:pass@127.0.0.1:1883/devices%2F%2B%2Ftelemetry?topology=cluster&clientId=gonavi-desktop&qos=1";
|
||||
}
|
||||
@@ -2108,6 +2156,8 @@ const ConnectionModal: React.FC<{
|
||||
return "timezone=Asia%2FShanghai";
|
||||
case "iotdb":
|
||||
return "fetchSize=1024&timeZone=Asia%2FShanghai";
|
||||
case "rocketmq":
|
||||
return "groupId=gonavi&namespace=prod&tag=TagA&pullBatchSize=32&startOffset=latest";
|
||||
case "mqtt":
|
||||
return "topics=devices%2F%2B%2Ftelemetry,%24SYS%2F%23&clientId=gonavi-desktop&qos=1&cleanSession=true&fetchWaitMs=4000";
|
||||
case "kafka":
|
||||
@@ -2236,6 +2286,26 @@ const ConnectionModal: React.FC<{
|
||||
return `mqtt://${encodedAuth}${allBrokers.join(",")}${topicPath}${query ? `?${query}` : ""}`;
|
||||
}
|
||||
|
||||
if (type === "rocketmq") {
|
||||
const primary = toAddress(host, port, defaultPort);
|
||||
const nameservers =
|
||||
values.rocketmqTopology === "cluster"
|
||||
? normalizeAddressList(values.rocketmqHosts, defaultPort)
|
||||
: [];
|
||||
const allNameServers = normalizeAddressList([primary, ...nameservers], defaultPort);
|
||||
const params = new URLSearchParams();
|
||||
if (allNameServers.length > 1 || values.rocketmqTopology === "cluster") {
|
||||
params.set("topology", "cluster");
|
||||
}
|
||||
if (Number.isFinite(timeout) && timeout > 0) {
|
||||
params.set("timeout", String(timeout));
|
||||
}
|
||||
mergeConnectionParams(params, values.connectionParams);
|
||||
const topicPath = database ? `/${encodeURIComponent(database)}` : "";
|
||||
const query = params.toString();
|
||||
return `rocketmq://${encodedAuth}${allNameServers.join(",")}${topicPath}${query ? `?${query}` : ""}`;
|
||||
}
|
||||
|
||||
if (type === "rabbitmq") {
|
||||
const address = toAddress(host, port, defaultPort);
|
||||
const params = new URLSearchParams();
|
||||
@@ -2629,6 +2699,8 @@ const ConnectionModal: React.FC<{
|
||||
configType === "sphinx"
|
||||
? normalizedHosts.slice(1)
|
||||
: [];
|
||||
const rocketmqHosts =
|
||||
configType === "rocketmq" ? normalizedHosts.slice(1) : [];
|
||||
const mqttHosts =
|
||||
configType === "mqtt" ? normalizedHosts.slice(1) : [];
|
||||
const kafkaHosts =
|
||||
@@ -2640,6 +2712,9 @@ const ConnectionModal: React.FC<{
|
||||
const mysqlIsReplica =
|
||||
String(config.topology || "").toLowerCase() === "replica" ||
|
||||
mysqlReplicaHosts.length > 0;
|
||||
const rocketmqIsCluster =
|
||||
String(config.topology || "").toLowerCase() === "cluster" ||
|
||||
rocketmqHosts.length > 0;
|
||||
const mqttIsCluster =
|
||||
String(config.topology || "").toLowerCase() === "cluster" ||
|
||||
mqttHosts.length > 0;
|
||||
@@ -2718,6 +2793,8 @@ const ConnectionModal: React.FC<{
|
||||
timeout: resolvedJvmTimeout,
|
||||
mysqlTopology: mysqlIsReplica ? "replica" : "single",
|
||||
mysqlReplicaHosts: mysqlReplicaHosts,
|
||||
rocketmqTopology: rocketmqIsCluster ? "cluster" : "single",
|
||||
rocketmqHosts: rocketmqHosts,
|
||||
mqttTopology: mqttIsCluster ? "cluster" : "single",
|
||||
mqttHosts: mqttHosts,
|
||||
kafkaTopology: kafkaIsCluster ? "cluster" : "single",
|
||||
@@ -3714,6 +3791,23 @@ const ConnectionModal: React.FC<{
|
||||
}
|
||||
}
|
||||
|
||||
if (type === "rocketmq") {
|
||||
const nameservers =
|
||||
mergedValues.rocketmqTopology === "cluster"
|
||||
? normalizeAddressList(mergedValues.rocketmqHosts, defaultPort)
|
||||
: [];
|
||||
const allHosts = normalizeAddressList(
|
||||
[`${primaryHost}:${primaryPort}`, ...nameservers],
|
||||
defaultPort,
|
||||
);
|
||||
if (mergedValues.rocketmqTopology === "cluster" || allHosts.length > 1) {
|
||||
hosts = allHosts;
|
||||
topology = "cluster";
|
||||
} else {
|
||||
topology = "single";
|
||||
}
|
||||
}
|
||||
|
||||
if (type === "mongodb") {
|
||||
mongoSrvEnabled = !!mergedValues.mongoSrv;
|
||||
const extraHosts =
|
||||
@@ -3950,6 +4044,7 @@ const ConnectionModal: React.FC<{
|
||||
includeDatabases: undefined,
|
||||
includeRedisDatabases: undefined,
|
||||
mysqlTopology: "single",
|
||||
rocketmqTopology: "single",
|
||||
mqttTopology: "single",
|
||||
kafkaTopology: "single",
|
||||
redisTopology: "single",
|
||||
@@ -3961,6 +4056,7 @@ const ConnectionModal: React.FC<{
|
||||
mongoAuthMechanism: "",
|
||||
savePassword: true,
|
||||
mysqlReplicaHosts: [],
|
||||
rocketmqHosts: [],
|
||||
mqttHosts: [],
|
||||
kafkaHosts: [],
|
||||
redisHosts: [],
|
||||
@@ -4016,6 +4112,8 @@ const ConnectionModal: React.FC<{
|
||||
httpTunnelUser: "",
|
||||
httpTunnelPassword: "",
|
||||
mysqlTopology: "single",
|
||||
rocketmqTopology: "single",
|
||||
mqttTopology: "single",
|
||||
kafkaTopology: "single",
|
||||
redisTopology: "single",
|
||||
mongoTopology: "single",
|
||||
@@ -4026,6 +4124,8 @@ const ConnectionModal: React.FC<{
|
||||
mongoAuthMechanism: "",
|
||||
savePassword: true,
|
||||
mysqlReplicaHosts: [],
|
||||
rocketmqHosts: [],
|
||||
mqttHosts: [],
|
||||
kafkaHosts: [],
|
||||
redisHosts: [],
|
||||
redisSentinelMaster: "",
|
||||
@@ -4041,7 +4141,7 @@ const ConnectionModal: React.FC<{
|
||||
});
|
||||
} else if (type !== "custom") {
|
||||
const defaultUser =
|
||||
type === "clickhouse" ? "default" : (type === "redis" || type === "elasticsearch" || type === "chroma" || type === "qdrant" || type === "mqtt" || type === "kafka" || type === "rabbitmq") ? "" : "root";
|
||||
type === "clickhouse" ? "default" : (type === "redis" || type === "elasticsearch" || type === "chroma" || type === "qdrant" || type === "rocketmq" || type === "mqtt" || type === "kafka" || type === "rabbitmq") ? "" : "root";
|
||||
const sslCapableType = supportsSSLForType(type);
|
||||
setUseSSL(false);
|
||||
setUseHttpTunnel(false);
|
||||
@@ -4060,6 +4160,7 @@ const ConnectionModal: React.FC<{
|
||||
httpTunnelUser: "",
|
||||
httpTunnelPassword: "",
|
||||
mysqlTopology: "single",
|
||||
rocketmqTopology: "single",
|
||||
mqttTopology: "single",
|
||||
kafkaTopology: "single",
|
||||
redisTopology: "single",
|
||||
@@ -4071,6 +4172,7 @@ const ConnectionModal: React.FC<{
|
||||
mongoAuthMechanism: "",
|
||||
savePassword: true,
|
||||
mysqlReplicaHosts: [],
|
||||
rocketmqHosts: [],
|
||||
mqttHosts: [],
|
||||
kafkaHosts: [],
|
||||
redisHosts: [],
|
||||
@@ -5189,6 +5291,22 @@ const ConnectionModal: React.FC<{
|
||||
),
|
||||
})}
|
||||
|
||||
{dbType === "rocketmq" &&
|
||||
renderConfigSectionCard({
|
||||
sectionKey: "service",
|
||||
icon: <DatabaseOutlined />,
|
||||
children: (
|
||||
<Form.Item
|
||||
name="database"
|
||||
label="默认 Topic(可选)"
|
||||
help="留空时必须在 SQL 中显式指定 Topic;连接参数可继续补充 groupId、namespace、tag、pullBatchSize 与 startOffset。"
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<Input {...noAutoCapInputProps} placeholder="例如:orders.events" />
|
||||
</Form.Item>
|
||||
),
|
||||
})}
|
||||
|
||||
{dbType === "mqtt" &&
|
||||
renderConfigSectionCard({
|
||||
sectionKey: "service",
|
||||
@@ -5295,6 +5413,28 @@ const ConnectionModal: React.FC<{
|
||||
}),
|
||||
})}
|
||||
|
||||
{isRocketMQ &&
|
||||
renderConfigSectionCard({
|
||||
sectionKey: "connectionMode",
|
||||
icon: <ClusterOutlined />,
|
||||
children: renderChoiceCards({
|
||||
fieldName: "rocketmqTopology",
|
||||
value: String(rocketmqTopology),
|
||||
options: [
|
||||
{
|
||||
value: "single",
|
||||
label: "单 NameServer",
|
||||
description: "只配置一个 NameServer,适合本地或简单环境。",
|
||||
},
|
||||
{
|
||||
value: "cluster",
|
||||
label: "集群模式",
|
||||
description: "配置多个 NameServer,提高路由发现与故障切换成功率。",
|
||||
},
|
||||
],
|
||||
}),
|
||||
})}
|
||||
|
||||
{isMQTT &&
|
||||
renderConfigSectionCard({
|
||||
sectionKey: "connectionMode",
|
||||
@@ -5337,6 +5477,26 @@ const ConnectionModal: React.FC<{
|
||||
),
|
||||
})}
|
||||
|
||||
{isRocketMQ &&
|
||||
rocketmqTopology === "cluster" &&
|
||||
renderConfigSectionCard({
|
||||
sectionKey: "replica",
|
||||
icon: <ClusterOutlined />,
|
||||
children: (
|
||||
<Form.Item
|
||||
name="rocketmqHosts"
|
||||
label="额外 NameServer 地址"
|
||||
help="可输入多个 NameServer 地址,格式:host:port(回车确认)"
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
placeholder="例如:10.10.0.12:9876、10.10.0.13:9876"
|
||||
tokenSeparators={[",", ";", " "]}
|
||||
/>
|
||||
</Form.Item>
|
||||
),
|
||||
})}
|
||||
|
||||
{isMQTT &&
|
||||
mqttTopology === "cluster" &&
|
||||
renderConfigSectionCard({
|
||||
@@ -5472,19 +5632,19 @@ const ConnectionModal: React.FC<{
|
||||
>
|
||||
<Form.Item
|
||||
name="user"
|
||||
label="用户名"
|
||||
label={dbType === "rocketmq" ? "Access Key" : "用户名"}
|
||||
rules={
|
||||
(dbType === "mongodb" || dbType === "elasticsearch" || dbType === "chroma" || dbType === "qdrant" || dbType === "kafka" || dbType === "rabbitmq")
|
||||
(dbType === "mongodb" || dbType === "elasticsearch" || dbType === "chroma" || dbType === "qdrant" || dbType === "rocketmq" || dbType === "kafka" || dbType === "rabbitmq")
|
||||
? []
|
||||
: [createUriAwareRequiredRule("请输入用户名")]
|
||||
}
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<Input {...noAutoCapInputProps} placeholder={(dbType === "elasticsearch" || dbType === "chroma" || dbType === "qdrant" || dbType === "mqtt" || dbType === "kafka" || dbType === "rabbitmq") ? "未开启认证可留空" : undefined} />
|
||||
<Input {...noAutoCapInputProps} placeholder={(dbType === "elasticsearch" || dbType === "chroma" || dbType === "qdrant" || dbType === "rocketmq" || dbType === "mqtt" || dbType === "kafka" || dbType === "rabbitmq") ? "未开启认证可留空" : undefined} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="password"
|
||||
label="密码"
|
||||
label={dbType === "rocketmq" ? "Secret Key" : "密码"}
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<Input.Password
|
||||
@@ -5496,8 +5656,8 @@ const ConnectionModal: React.FC<{
|
||||
placeholder={getStoredSecretPlaceholder({
|
||||
hasStoredSecret:
|
||||
initialValues?.hasPrimaryPassword,
|
||||
emptyPlaceholder: "密码",
|
||||
retainedLabel: "已保存密码",
|
||||
emptyPlaceholder: dbType === "rocketmq" ? "未开启认证可留空" : "密码",
|
||||
retainedLabel: dbType === "rocketmq" ? "已保存 Secret Key" : "已保存密码",
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
@@ -6402,6 +6562,7 @@ const ConnectionModal: React.FC<{
|
||||
connectionParams: "",
|
||||
oceanBaseProtocol: "mysql",
|
||||
mysqlTopology: "single",
|
||||
rocketmqTopology: "single",
|
||||
mqttTopology: "single",
|
||||
kafkaTopology: "single",
|
||||
redisTopology: "single",
|
||||
@@ -6411,6 +6572,7 @@ const ConnectionModal: React.FC<{
|
||||
mongoAuthMechanism: "",
|
||||
savePassword: true,
|
||||
mysqlReplicaHosts: [],
|
||||
rocketmqHosts: [],
|
||||
mqttHosts: [],
|
||||
kafkaHosts: [],
|
||||
redisHosts: [],
|
||||
|
||||
@@ -39,6 +39,13 @@ describe('DatabaseIcons', () => {
|
||||
expect(markup).toContain('>Io</text>');
|
||||
});
|
||||
|
||||
it('includes RocketMQ in the selectable database icons', () => {
|
||||
expect(DB_ICON_TYPES).toContain('rocketmq');
|
||||
expect(getDbIconLabel('rocketmq')).toBe('RocketMQ');
|
||||
const markup = renderToStaticMarkup(<>{getDbIcon('rocketmq', undefined, 22)}</>);
|
||||
expect(markup).toContain('>Rm</text>');
|
||||
});
|
||||
|
||||
it('includes MQTT in the selectable database icons', () => {
|
||||
expect(DB_ICON_TYPES).toContain('mqtt');
|
||||
expect(getDbIconLabel('mqtt')).toBe('MQTT');
|
||||
|
||||
@@ -52,6 +52,7 @@ const DB_DEFAULT_COLORS: Record<string, string> = {
|
||||
iris: '#1F6FEB',
|
||||
tdengine: '#2962FF',
|
||||
iotdb: '#0F766E',
|
||||
rocketmq: '#EA580C',
|
||||
mqtt: '#0EA5A4',
|
||||
kafka: '#F97316',
|
||||
rabbitmq: '#FF6B35',
|
||||
@@ -195,6 +196,9 @@ const TDengineIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
|
||||
const IoTDBIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
|
||||
<ColorBadge size={size} color={color || DB_DEFAULT_COLORS.iotdb} label="Io" />
|
||||
);
|
||||
const RocketMQIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
|
||||
<ColorBadge size={size} color={color || DB_DEFAULT_COLORS.rocketmq} label="Rm" />
|
||||
);
|
||||
const MQTTIcon: React.FC<DbIconProps> = ({ size = 16, color }) => (
|
||||
<ColorBadge size={size} color={color || DB_DEFAULT_COLORS.mqtt} label="Mq" />
|
||||
);
|
||||
@@ -266,6 +270,7 @@ const DB_ICON_MAP: Record<string, React.FC<DbIconProps>> = {
|
||||
iris: IrisIcon,
|
||||
tdengine: TDengineIcon,
|
||||
iotdb: IoTDBIcon,
|
||||
rocketmq: RocketMQIcon,
|
||||
mqtt: MQTTIcon,
|
||||
kafka: KafkaIcon,
|
||||
rabbitmq: RabbitMQIcon,
|
||||
@@ -279,7 +284,7 @@ const DB_ICON_MAP: Record<string, React.FC<DbIconProps>> = {
|
||||
export const DB_ICON_TYPES: string[] = [
|
||||
'mysql', 'mariadb', 'oceanbase', 'postgres', 'redis', 'mongodb', 'jvm',
|
||||
'oracle', 'sqlserver', 'sqlite', 'duckdb', 'clickhouse', 'starrocks',
|
||||
'kingbase', 'dameng', 'vastbase', 'opengauss', 'gaussdb', 'goldendb', 'highgo', 'iris', 'tdengine', 'iotdb', 'mqtt', 'kafka', 'rabbitmq', 'chroma', 'qdrant', 'elasticsearch', 'custom',
|
||||
'kingbase', 'dameng', 'vastbase', 'opengauss', 'gaussdb', 'goldendb', 'highgo', 'iris', 'tdengine', 'iotdb', 'rocketmq', 'mqtt', 'kafka', 'rabbitmq', 'chroma', 'qdrant', 'elasticsearch', 'custom',
|
||||
];
|
||||
|
||||
/** 该类型是否有品牌 SVG 文件 */
|
||||
@@ -301,7 +306,7 @@ export const getDbIconLabel = (type: string): string => {
|
||||
sqlserver: 'SQL Server', clickhouse: 'ClickHouse', sqlite: 'SQLite',
|
||||
starrocks: 'StarRocks',
|
||||
duckdb: 'DuckDB', kingbase: '金仓', dameng: '达梦',
|
||||
vastbase: 'VastBase', opengauss: 'OpenGauss', gaussdb: 'GaussDB', goldendb: 'GoldenDB', highgo: '瀚高', iris: 'InterSystems IRIS', tdengine: 'TDengine', iotdb: 'Apache IoTDB', mqtt: 'MQTT', kafka: 'Kafka', rabbitmq: 'RabbitMQ',
|
||||
vastbase: 'VastBase', opengauss: 'OpenGauss', gaussdb: 'GaussDB', goldendb: 'GoldenDB', highgo: '瀚高', iris: 'InterSystems IRIS', tdengine: 'TDengine', iotdb: 'Apache IoTDB', rocketmq: 'RocketMQ', mqtt: 'MQTT', kafka: 'Kafka', rabbitmq: 'RabbitMQ',
|
||||
chroma: 'Chroma',
|
||||
qdrant: 'Qdrant',
|
||||
elasticsearch: 'Elasticsearch',
|
||||
|
||||
@@ -14,6 +14,28 @@ import {
|
||||
const { Text } = Typography;
|
||||
const { TextArea } = Input;
|
||||
|
||||
const ROCKETMQ_DELAY_LEVEL_OPTIONS = [
|
||||
{ label: '不延时', value: 0 },
|
||||
{ label: '1 · 1s', value: 1 },
|
||||
{ label: '2 · 5s', value: 2 },
|
||||
{ label: '3 · 10s', value: 3 },
|
||||
{ label: '4 · 30s', value: 4 },
|
||||
{ label: '5 · 1m', value: 5 },
|
||||
{ label: '6 · 2m', value: 6 },
|
||||
{ label: '7 · 3m', value: 7 },
|
||||
{ label: '8 · 4m', value: 8 },
|
||||
{ label: '9 · 5m', value: 9 },
|
||||
{ label: '10 · 6m', value: 10 },
|
||||
{ label: '11 · 7m', value: 11 },
|
||||
{ label: '12 · 8m', value: 12 },
|
||||
{ label: '13 · 9m', value: 13 },
|
||||
{ label: '14 · 10m', value: 14 },
|
||||
{ label: '15 · 20m', value: 15 },
|
||||
{ label: '16 · 30m', value: 16 },
|
||||
{ label: '17 · 1h', value: 17 },
|
||||
{ label: '18 · 2h', value: 18 },
|
||||
];
|
||||
|
||||
export type MessagePublishModalProps = {
|
||||
open: boolean;
|
||||
connection: SavedConnection | null;
|
||||
@@ -171,22 +193,48 @@ const MessagePublishModal: React.FC<MessagePublishModalProps> = ({
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{presentation.showTag && (
|
||||
<Form.Item
|
||||
label="Tag(可选)"
|
||||
name="tag"
|
||||
extra="留空表示不过滤或不写入 Tag。"
|
||||
>
|
||||
<Input placeholder={presentation.tagPlaceholder} />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{presentation.showDelayLevel && (
|
||||
<Form.Item
|
||||
label="Delay Level(可选)"
|
||||
name="delayLevel"
|
||||
extra="RocketMQ 使用固定延时级别,0 表示立即发送。"
|
||||
>
|
||||
<Select options={ROCKETMQ_DELAY_LEVEL_OPTIONS} />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{presentation.showKey && (
|
||||
<Form.Item label="消息 Key(可选)">
|
||||
<Space.Compact style={{ width: '100%' }}>
|
||||
<Form.Item name="keyMode" noStyle>
|
||||
<Select
|
||||
style={{ width: 120 }}
|
||||
options={[
|
||||
{ label: '文本', value: 'text' },
|
||||
{ label: 'JSON', value: 'json' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label={presentation.keyLabel}>
|
||||
{presentation.showKeyMode ? (
|
||||
<Space.Compact style={{ width: '100%' }}>
|
||||
<Form.Item name="keyMode" noStyle>
|
||||
<Select
|
||||
style={{ width: 120 }}
|
||||
options={[
|
||||
{ label: '文本', value: 'text' },
|
||||
{ label: 'JSON', value: 'json' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="key" noStyle>
|
||||
<Input placeholder={presentation.keyPlaceholder} />
|
||||
</Form.Item>
|
||||
</Space.Compact>
|
||||
) : (
|
||||
<Form.Item name="key" noStyle>
|
||||
<Input placeholder="可留空;JSON 模式请输入一行合法 JSON" />
|
||||
<Input placeholder={presentation.keyPlaceholder} />
|
||||
</Form.Item>
|
||||
</Space.Compact>
|
||||
)}
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
@@ -208,13 +256,15 @@ const MessagePublishModal: React.FC<MessagePublishModalProps> = ({
|
||||
<TextArea rows={8} placeholder="请输入消息体" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Headers(可选)"
|
||||
name="headers"
|
||||
extra={'需为 JSON 对象,例如 {"x-source":"gonavi"}。'}
|
||||
>
|
||||
<TextArea rows={5} placeholder='{"x-source":"gonavi"}' />
|
||||
</Form.Item>
|
||||
{presentation.showHeaders && (
|
||||
<Form.Item
|
||||
label="Headers(可选)"
|
||||
name="headers"
|
||||
extra={'需为 JSON 对象,例如 {"x-source":"gonavi"}。'}
|
||||
>
|
||||
<TextArea rows={5} placeholder='{"x-source":"gonavi"}' />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{presentation.showProperties && (
|
||||
<Form.Item
|
||||
|
||||
Reference in New Issue
Block a user