import { Check, ChevronDown } from 'lucide-react' import { toast } from 'sonner' import { Alert, AlertDescription, AlertTitle, Collapsible, CollapsibleContent, CollapsibleTrigger, Dialog, DialogContent, DialogDescription, DialogHeader, DialogSection, DialogSectionSeparator, DialogTitle, WarningIcon, } from 'ui' import { CodeBlock } from 'ui-patterns/CodeBlock' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' import { DocsButton } from '@/components/ui/DocsButton' import InformationBox from '@/components/ui/InformationBox' import { useCreateAndExposeAPISchemaMutation } from '@/data/api-settings/create-and-expose-api-schema-mutation' import { useProjectPostgrestConfigQuery } from '@/data/config/project-postgrest-config-query' import { useProjectPostgrestConfigUpdateMutation } from '@/data/config/project-postgrest-config-update-mutation' import { useSchemasQuery } from '@/data/database/schemas-query' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' import { DOCS_URL } from '@/lib/constants' interface HardenAPIModalProps { visible: boolean onClose: () => void } export const HardenAPIModal = ({ visible, onClose }: HardenAPIModalProps) => { const { data: project } = useSelectedProjectQuery() const { data: schemas } = useSchemasQuery({ projectRef: project?.ref, connectionString: project?.connectionString, }) const { data: config } = useProjectPostgrestConfigQuery({ projectRef: project?.ref }) const hasAPISchema = (schemas ?? []).find((schema) => schema.name === 'api') const exposedSchemas = config?.db_schema.split(',').map((x) => x.trim()) ?? [] const isAPISchemaExposed = exposedSchemas.includes('api') const isPublicSchemaExposed = exposedSchemas.includes('public') const { mutate: createAndExposeAPISchema, isPending: isCreatingAPISchema } = useCreateAndExposeAPISchemaMutation({ onSuccess: () => { toast.success(`Successfully created api schema and exposed via Data API`) }, }) const { mutate: updatePostgrestConfig, isPending: isUpdatingConfig } = useProjectPostgrestConfigUpdateMutation({ onSuccess: () => { toast.success('Success removed public schema from exposed schemas') }, }) const onSelectCreateAndExposeAPISchema = () => { if (project === undefined) return console.error('Project is required') if (config === undefined) return console.error('Postgrest config is required') createAndExposeAPISchema({ projectRef: project?.ref, connectionString: project?.connectionString, existingPostgrestConfig: { max_rows: config.max_rows, db_pool: config.db_pool, db_schema: config.db_schema, db_extra_search_path: config?.db_extra_search_path, }, }) } const onSelectRemovePublicSchema = () => { if (project === undefined) return console.error('Project is required') if (config === undefined) return console.error('Postgrest config is required') const updatedDbExtraSearchPath = config.db_extra_search_path .split(',') .map((x) => x.trim()) .filter((x) => x !== 'public') .join(', ') const updatedDbSchema = config.db_schema .split(',') .map((x) => x.trim()) .filter((x) => x !== 'public') .join(', ') updatePostgrestConfig({ projectRef: project.ref, maxRows: config.max_rows, dbPool: config.db_pool, dbSchema: updatedDbSchema, dbExtraSearchPath: updatedDbExtraSearchPath, }) } return ( Switch the default API schema Expose a custom schema instead of the public{' '} schema

By default, the public schema is used to generate API routes. In some cases, it's better to use a custom schema. This is important if you use tools that generate tables in the{' '} public schema to{' '} prevent accidental exposure of data.

1. Create a custom api schema and expose it

{hasAPISchema && isAPISchemaExposed ? ( ) : ( )}

Click the button below to create a new schema named{' '} api and grant the{' '} anon and{' '} authenticated roles usage privileges on this schema. This schema will thereafter also be exposed to the Data API.

The following query will be run to create the{' '} api schema , as well as to grant the necessary privileges to the respective roles

{`create schema if not exists api;\ngrant usage on schema api to anon, authenticated;`}
} /> Create and expose schema to Data API

Under these new settings, the anon and{' '} authenticated roles can execute functions defined in the api schema, but they have no automatic permissions on any tables. On a table-by-table basis, you can grant them permissions by running the following command:

{`grant select on table api. to anon;\ngrant select, insert, update, delete on table api. to authenticated;`}

2. Remove the public schema from the exposed schemas

{!isPublicSchemaExposed ? ( ) : ( )}
Ensure that your app is no longer using the{' '} public schema The public schema will not be accessible via the API once its not exposed. You should be using the{' '} api schema instead.

Click the button below to remove the{' '} public schema from both Exposed schemas and Extra search path in your API configuration.

Remove public schema from exposed schemas
) }