import { useParams } from 'common' import { isEmpty } from 'lodash' import Link from 'next/link' import { useCallback, useState } from 'react' import { toast } from 'sonner' import { Button, Card, CardContent } from 'ui' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import { PolicyTableRow, PolicyTableRowProps, } from '@/components/interfaces/Auth/Policies/PolicyTableRow' import type { Policy } from '@/components/interfaces/Auth/Policies/PolicyTableRow/PolicyTableRow.utils' import { ProtectedSchemaWarning } from '@/components/interfaces/Database/ProtectedSchemaWarning' import { NoSearchResults } from '@/components/ui/NoSearchResults' import { useDatabasePolicyDeleteMutation } from '@/data/database-policies/database-policy-delete-mutation' import { useTableUpdateMutation } from '@/data/tables/table-update-mutation' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' interface PoliciesProps { search?: string schema: string tables: PolicyTableRowProps['table'][] hasTables: boolean isLocked: boolean visibleTableIds: Set onSelectCreatePolicy: (table: string) => void onSelectEditPolicy: (policy: Policy) => void onResetSearch?: () => void } export const Policies = ({ search, schema, tables, hasTables, isLocked, visibleTableIds, onSelectCreatePolicy, onSelectEditPolicy: onSelectEditPolicyAI, onResetSearch, }: PoliciesProps) => { const { ref } = useParams() const { data: project } = useSelectedProjectQuery() const [selectedTableToToggleRLS, setSelectedTableToToggleRLS] = useState<{ id: number schema: string name: string rls_enabled: boolean }>() const [selectedPolicyToDelete, setSelectedPolicyToDelete] = useState({}) const { mutate: updateTable, isPending: isUpdatingTable } = useTableUpdateMutation({ onError: (error) => { toast.error(`Failed to toggle RLS: ${error.message}`) }, onSettled: () => { closeConfirmModal() }, }) const { mutate: deleteDatabasePolicy, isPending: isDeletingPolicy } = useDatabasePolicyDeleteMutation({ onSuccess: () => { toast.success('Successfully deleted policy!') }, onSettled: () => { closeConfirmModal() }, }) const closeConfirmModal = useCallback(() => { setSelectedPolicyToDelete({}) setSelectedTableToToggleRLS(undefined) }, []) const onSelectToggleRLS = useCallback( (table: { id: number; schema: string; name: string; rls_enabled: boolean }) => { setSelectedTableToToggleRLS(table) }, [] ) const onSelectEditPolicy = useCallback( (policy: Policy) => { onSelectEditPolicyAI(policy) }, [onSelectEditPolicyAI] ) const onSelectDeletePolicy = useCallback((policy: Policy) => { setSelectedPolicyToDelete(policy) }, []) // Methods that involve some API const onToggleRLS = async () => { if (!selectedTableToToggleRLS) return console.error('Table is required') const payload = { id: selectedTableToToggleRLS.id, rls_enabled: !selectedTableToToggleRLS.rls_enabled, } updateTable({ projectRef: project?.ref!, connectionString: project?.connectionString, id: selectedTableToToggleRLS.id, name: selectedTableToToggleRLS.name, schema: selectedTableToToggleRLS.schema, payload: payload, }) } const onDeletePolicy = async () => { if (!project) return console.error('Project is required') deleteDatabasePolicy({ projectRef: project.ref, connectionString: project.connectionString, originalPolicy: selectedPolicyToDelete, }) } const handleCreatePolicy = useCallback( (tableData: PolicyTableRowProps['table']) => { onSelectCreatePolicy(tableData.name) }, [onSelectCreatePolicy] ) if (!hasTables) { return (

No tables to create policies for

RLS Policies control per-user access to table rows. Create a table in this schema first before creating a policy.

) } return ( <>
{isLocked && } {tables.length > 0 ? ( <> {tables.map((table) => { const isVisible = visibleTableIds.has(table.id) return ( ) })} {!!search && visibleTableIds.size === 0 && ( )} ) : hasTables ? ( ) : null}
) }