feat(rabbitmq): add message queue management support

This commit is contained in:
Yann丶 authored and GitHub committed 2026-07-22 23:52:09 +08:00
1 parent 503d6748d4
commit 39e554369f
76 files changed
+14915 -707

No files matched your search

@@ -17,4 +17,21 @@ describe("mqAuth", () => {
}),
).toBe("basic");
});
it("allows basic auth for RabbitMQ", () => {
expect(isMqAuthKindAllowedForSystem("rabbitmq", "basic")).toBe(true);
expect(isMqAuthKindAllowedForSystem("rabbitmq", "kerberos")).toBe(false);
expect(isMqAuthKindAllowedForSystem("rabbitmq", "token")).toBe(false);
});
it("detects RabbitMQ basic auth from config", () => {
expect(
detectMqUiAuthKind({
systemKind: "rabbitmq",
authKind: "basic",
saslMechanism: "",
jaasConfig: "",
}),
).toBe("basic");
});
});
@@ -0,0 +1,33 @@
import { describe, expect, it } from "vitest";
import { normalizeRabbitmqAddresses } from "@/lib/connection/rabbitmqAddresses";
describe("RabbitMQ addresses", () => {
it("keeps comma-separated addresses", () => {
expect(normalizeRabbitmqAddresses("node1:5672, node2:5672")).toBe("node1:5672,node2:5672");
});
it("appends the default AMQP port when missing", () => {
expect(normalizeRabbitmqAddresses("127.0.0.1")).toBe("127.0.0.1:5672");
expect(normalizeRabbitmqAddresses("node1, node2:5673")).toBe("node1:5672,node2:5673");
});
it("normalizes common address separators to commas", () => {
expect(normalizeRabbitmqAddresses("node1:5672;node2:5672,node3:5672\nnode4:5672 node5:5672")).toBe("node1:5672,node2:5672,node3:5672,node4:5672,node5:5672");
});
it("keeps IPv6 addresses", () => {
expect(normalizeRabbitmqAddresses("[::1]:5672;[2001:db8::1]:5672")).toBe("[::1]:5672,[2001:db8::1]:5672");
});
it("rejects empty addresses", () => {
expect(() => normalizeRabbitmqAddresses(" ")).toThrow("RabbitMQ addresses are required");
});
it("rejects addresses with URL schemes", () => {
expect(() => normalizeRabbitmqAddresses("amqp://node1:5672,node2:5672")).toThrow("RabbitMQ addresses must be host:port values without a URL scheme");
});
it("rejects invalid address values", () => {
expect(() => normalizeRabbitmqAddresses("node1:5672/path,node2:5672")).toThrow("RabbitMQ addresses are invalid");
});
});
@@ -15,6 +15,7 @@ export function agentDriverInstallKey(dbType: DatabaseType | undefined, driverPr
if (dbType === "mq") {
if (driverProfile === "kafka") return "kafka";
if (driverProfile === "rocketmq") return "rocketmq";
if (driverProfile === "rabbitmq") return "rabbitmq";
return undefined;
}
return driverProfile && driverProfile !== dbType ? driverProfile : dbType;
@@ -5,10 +5,12 @@ export type MqUiAuthKind = MqAuthKind | "kerberos";
const KAFKA_AUTH_KINDS = new Set<MqUiAuthKind>(["none", "basic", "kerberos"]);
const ROCKETMQ_AUTH_KINDS = new Set<MqUiAuthKind>(["none", "basic"]);
const RABBITMQ_AUTH_KINDS = new Set<MqUiAuthKind>(["none", "basic"]);
export function isMqAuthKindAllowedForSystem(systemKind: MqSystemKind, authKind: MqUiAuthKind): boolean {
if (systemKind === "kafka") return KAFKA_AUTH_KINDS.has(authKind);
if (systemKind === "rocketmq") return ROCKETMQ_AUTH_KINDS.has(authKind);
if (systemKind === "rabbitmq") return RABBITMQ_AUTH_KINDS.has(authKind);
return authKind !== "kerberos";
}
@@ -22,6 +24,9 @@ export function detectMqUiAuthKind({ systemKind, authKind, saslMechanism, jaasCo
if (systemKind === "rocketmq") {
return authKind === "basic" ? "basic" : "none";
}
if (systemKind === "rabbitmq") {
return authKind === "basic" ? "basic" : "none";
}
return authKind || "none";
}
@@ -0,0 +1,34 @@
const RABBITMQ_ADDRESS_SEPARATOR = /[\s,;,;]+/u;
const RABBITMQ_DEFAULT_PORT = "5672";
function requireRabbitmqAddresses(value: string): string {
const trimmed = value.trim();
if (!trimmed) throw new Error("RabbitMQ addresses are required");
return trimmed;
}
function normalizeRabbitmqAddress(address: string): string {
if (address.includes("://")) {
throw new Error("RabbitMQ addresses must be host:port values without a URL scheme");
}
let parsed: URL;
try {
parsed = new URL(`amqp://${address}`);
} catch {
throw new Error("RabbitMQ addresses are invalid");
}
if (!parsed.hostname || parsed.username || parsed.password || parsed.search || parsed.hash || (parsed.pathname && parsed.pathname !== "/")) {
throw new Error("RabbitMQ addresses are invalid");
}
return parsed.port ? address : `${address}:${RABBITMQ_DEFAULT_PORT}`;
}
export function normalizeRabbitmqAddresses(value: string): string {
const addresses = requireRabbitmqAddresses(value)
.split(RABBITMQ_ADDRESS_SEPARATOR)
.map((address) => address.trim())
.filter(Boolean)
.map(normalizeRabbitmqAddress);
if (!addresses.length) throw new Error("RabbitMQ addresses are required");
return addresses.join(",");
}