import { useParams } from 'common' import { Lock, Paintbrush, PlusCircle, Trash2 } from 'lucide-react' import Link from 'next/link' import { parseAsBoolean, useQueryState } from 'nuqs' import { useMemo, useState } from 'react' import { toast } from 'sonner' import { Button, cn, LoadingLine, Popover, PopoverContent, PopoverTrigger, Separator } from 'ui' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader' import { pgmqQueueTable } from './Queues.utils' import { DeleteQueue } from '@/components/interfaces/Integrations/Queues/SingleQueue/DeleteQueue' import { PurgeQueue } from '@/components/interfaces/Integrations/Queues/SingleQueue/PurgeQueue' import { QUEUE_MESSAGE_TYPE } from '@/components/interfaces/Integrations/Queues/SingleQueue/Queue.utils' import { QueueMessagesDataGrid } from '@/components/interfaces/Integrations/Queues/SingleQueue/QueueDataGrid' import { QueueFilters } from '@/components/interfaces/Integrations/Queues/SingleQueue/QueueFilters' import { QueueSettings } from '@/components/interfaces/Integrations/Queues/SingleQueue/QueueSettings' import { SendMessageModal } from '@/components/interfaces/Integrations/Queues/SingleQueue/SendMessageModal' import { Markdown } from '@/components/interfaces/Markdown' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' import { useDatabasePoliciesQuery } from '@/data/database-policies/database-policies-query' import { useQueueMessagesInfiniteQuery } from '@/data/database-queues/database-queue-messages-infinite-query' import { useQueuesExposePostgrestStatusQuery } from '@/data/database-queues/database-queues-expose-postgrest-status-query' import { useTableUpdateMutation } from '@/data/tables/table-update-mutation' import { useTablesQuery } from '@/data/tables/tables-query' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' export const QueueTab = () => { const { childId: queueName, ref } = useParams() const { data: project } = useSelectedProjectQuery() const [openRlsPopover, setOpenRlsPopover] = useState(false) const [rlsConfirmModalOpen, setRlsConfirmModalOpen] = useState(false) const [sendMessageModalShown, setSendMessageModalShown] = useQueryState( 'new-message', parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true }) ) const [purgeQueueModalShown, setPurgeQueueModalShown] = useState(false) const [deleteQueueModalShown, setDeleteQueueModalShown] = useState(false) const [selectedTypes, setSelectedTypes] = useState([]) const { data: tables, isPending: isLoadingTables } = useTablesQuery({ projectRef: project?.ref, connectionString: project?.connectionString, schema: 'pgmq', }) const queueRelname = queueName ? pgmqQueueTable(queueName) : undefined const queueTable = tables?.find((x) => x.name === queueRelname) const isRlsEnabled = queueTable?.rls_enabled ?? false const { data: policies } = useDatabasePoliciesQuery({ projectRef: project?.ref, connectionString: project?.connectionString, schema: 'pgmq', }) const queuePolicies = (policies ?? []).filter((policy) => policy.table === queueRelname) const { data: isExposed } = useQueuesExposePostgrestStatusQuery({ projectRef: project?.ref, connectionString: project?.connectionString, }) const { data, error, isPending: isLoading, fetchNextPage, isFetching, } = useQueueMessagesInfiniteQuery( { projectRef: project?.ref, connectionString: project?.connectionString, queueName: queueName!, // when no types are selected, include all types of messages status: selectedTypes.length === 0 ? ['archived', 'available', 'scheduled'] : selectedTypes, }, { staleTime: 30 } ) const messages = useMemo(() => data?.pages.flatMap((p) => p), [data?.pages]) const { mutate: updateTable, isPending: isUpdatingTable } = useTableUpdateMutation({ onSettled: () => { toast.success(`Successfully enabled RLS for ${queueName}`) setRlsConfirmModalOpen(false) }, }) const onToggleRLS = async () => { if (!project) return console.error('Project is required') if (!queueTable) return toast.error('Unable to toggle RLS: Queue table not found') const payload = { id: queueTable.id, rls_enabled: true, } updateTable({ projectRef: project?.ref, connectionString: project?.connectionString, id: queueTable.id, name: queueTable.name, schema: 'pgmq', payload: payload, }) } return (
setPurgeQueueModalShown(true)} icon={} title="Purge messages" aria-label="Purge messages" tooltip={{ content: { side: 'bottom', text: 'Purge messages' } }} /> setDeleteQueueModalShown(true)} icon={} title="Delete queue" aria-label="Delete queue" tooltip={{ content: { side: 'bottom', text: 'Delete queue' } }} /> {isLoadingTables ? ( ) : isRlsEnabled ? ( <> {queuePolicies.length === 0 ? ( } tooltip={{ content: { side: 'bottom', className: 'w-[280px]', text: 'RLS is enabled for this queue, but no policies are set. Queue will not be accessible.', }, }} > Add RLS policy ) : (
} > Auth {queuePolicies.length > 1 ? 'policies' : 'policy'} )} ) : ( setOpenRlsPopover(!openRlsPopover)} >

Row Level Security (RLS)

{isExposed ? ( <>

You can restrict and control who can manage this queue using Row Level Security.

With RLS enabled, anonymous users will not have access to this queue.

) : ( <> )}
)} {/* } */}
setSendMessageModalShown(true)} fetchNextPage={fetchNextPage} /> setSendMessageModalShown(false)} /> setDeleteQueueModalShown(false)} /> setPurgeQueueModalShown(false)} /> setRlsConfirmModalOpen(false)} onConfirm={() => onToggleRLS()} >

Are you sure you want to enable Row Level Security for the queue "{queueName}"?

) }