import {useEffect, useState} from "react"; import {Activity, Loader2, MessageSquare, Network, Plus, RefreshCw, Send, Server, Trash2} from "lucide-react"; import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query"; import {createMskCluster, createMskTopic, deleteMskCluster, deleteMskTopic, getMskConsumerGroupLag, listMskClusters, listMskTopics, peekMskMessages, produceMskMessage} from "@/api/mskClient"; import {listEc2Subnets} from "@/api/aws/ec2.api"; export function MskPanel({runtimeReachable}: {runtimeReachable: boolean}) { const queryClient = useQueryClient(); const [clusterArn, setClusterArn] = useState(""); const [deleteClusterConfirmation, setDeleteClusterConfirmation] = useState(false); const [clusterName, setClusterName] = useState(""); const [brokers, setBrokers] = useState("1"); const [version, setVersion] = useState("3.7.x"); const [subnets, setSubnets] = useState([]); const [topic, setTopic] = useState(""); const [partitions, setPartitions] = useState("1"); const [retentionMs, setRetentionMs] = useState(""); const [messageTopic, setMessageTopic] = useState(""); const [messageKey, setMessageKey] = useState(""); const [messageValue, setMessageValue] = useState(""); const [groupId, setGroupId] = useState(""); const [messages, setMessages] = useState>>([]); const [lag, setLag] = useState>>([]); const clustersQuery = useQuery({queryKey: ["msk-clusters"], queryFn: ({signal}) => listMskClusters(signal), enabled: runtimeReachable}); const subnetsQuery = useQuery({queryKey: ["ec2", "subnets"], queryFn: ({signal}) => listEc2Subnets(undefined, signal), enabled: runtimeReachable}); const topicsQuery = useQuery({queryKey: ["msk-topics", clusterArn], queryFn: ({signal}) => listMskTopics(clusterArn, signal), enabled: runtimeReachable && Boolean(clusterArn)}); useEffect(() => { const firstArn = clustersQuery.data?.[0]?.ClusterArn; if (!clusterArn && firstArn) setClusterArn(firstArn); }, [clusterArn, clustersQuery.data]); useEffect(() => { setMessageTopic(""); setMessages([]); setLag([]); }, [clusterArn]); const createCluster = useMutation({mutationFn: () => createMskCluster({clusterName: clusterName.trim(), kafkaVersion: version.trim(), numberOfBrokerNodes: Number(brokers), brokerNodeGroupInfo: {clientSubnets: subnets}}), onSuccess: () => { setClusterName(""); void queryClient.invalidateQueries({queryKey: ["msk-clusters"]}); }}); const deleteCluster = useMutation({mutationFn: () => deleteMskCluster(clusterArn), onSuccess: () => { setClusterArn(""); setDeleteClusterConfirmation(false); void queryClient.invalidateQueries({queryKey: ["msk-clusters"]}); }}); const createTopic = useMutation({mutationFn: () => createMskTopic(clusterArn, {topic: topic.trim(), numPartitions: Number(partitions), ...(retentionMs.trim() ? {configEntries: [{name: "retention.ms", value: retentionMs.trim()}]} : {})}), onSuccess: () => { setTopic(""); setRetentionMs(""); void queryClient.invalidateQueries({queryKey: ["msk-topics", clusterArn]}); }}); const deleteTopic = useMutation({mutationFn: (topicName: string) => deleteMskTopic(clusterArn, topicName), onSuccess: () => { setMessages([]); void queryClient.invalidateQueries({queryKey: ["msk-topics", clusterArn]}); }}); const produce = useMutation({mutationFn: () => produceMskMessage(clusterArn, messageTopic, {value: messageValue, ...(messageKey.trim() ? {key: messageKey} : {})}), onSuccess: () => { setMessageKey(""); setMessageValue(""); }}); const peek = useMutation({mutationFn: () => peekMskMessages(clusterArn, messageTopic, 50), onSuccess: setMessages}); const loadLag = useMutation({mutationFn: () => getMskConsumerGroupLag(clusterArn, groupId.trim(), messageTopic || undefined), onSuccess: setLag}); const error = createCluster.error ?? deleteCluster.error ?? createTopic.error ?? deleteTopic.error ?? produce.error ?? peek.error ?? loadLag.error; return

Amazon MSK

Kafka clusters

Provision broker nodes in existing EC2 subnets for local Kafka workloads.

{ event.preventDefault(); if (clusterName.trim() && Number(brokers) > 0 && version.trim() && subnets.length > 0) createCluster.mutate(); }}>
Broker access is open: no IAM, SASL, or TLS authentication is configured.
{clusterArn && (deleteClusterConfirmation ?
Delete this cluster and all topics?
: )}

Topics

Kafka Admin API

Define partitions and optional retention for the active cluster.

{ event.preventDefault(); if (clusterArn && topic.trim() && Number(partitions) > 0) createTopic.mutate(); }}>
{topicsQuery.isLoading &&

Loading topics...

}{topicsQuery.data?.length === 0 &&

No topics found for this cluster.

}{topicsQuery.data?.map((item) =>
{item.name}{item.partitionCount} partition{item.partitionCount === 1 ? "" : "s"}
)}

Messages

Produce and inspect

Publish records or peek from the beginning with a non-committing consumer.

{ event.preventDefault(); if (clusterArn && messageTopic && messageValue) produce.mutate(); }}>