import { zodResolver } from '@hookform/resolvers/zod' import { PermissionAction } from '@supabase/shared-types/out/constants' import { IS_PLATFORM, useParams } from 'common' import { useRouter } from 'next/router' import { useEffect, useMemo, useState } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' import { toast } from 'sonner' import { Alert, AlertDescription, AlertTitle, Button, Card, CardContent, CardFooter, cn, copyToClipboard, CriticalIcon, Form, FormControl, FormField, Switch, Tabs_Shadcn_ as Tabs, TabsContent_Shadcn_ as TabsContent, TabsList_Shadcn_ as TabsList, TabsTrigger_Shadcn_ as TabsTrigger, } from 'ui' import { CodeBlock } from 'ui-patterns/CodeBlock' import { Input } from 'ui-patterns/DataInputs/Input' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import { PageContainer } from 'ui-patterns/PageContainer' import { PageSection, PageSectionContent, PageSectionMeta, PageSectionSummary, PageSectionTitle, } from 'ui-patterns/PageSection' import z from 'zod' import CommandRender from '../CommandRender' import { INVOCATION_TABS } from './EdgeFunctionDetails.constants' import { generateCLICommands } from './EdgeFunctionDetails.utils' import { useAPIKeys } from '@/data/api-keys/api-keys-query' import { useProjectApiUrl } from '@/data/config/project-endpoint-query' import { useEdgeFunctionQuery } from '@/data/edge-functions/edge-function-query' import { useEdgeFunctionDeleteMutation } from '@/data/edge-functions/edge-functions-delete-mutation' import { useEdgeFunctionUpdateMutation } from '@/data/edge-functions/edge-functions-update-mutation' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' const FormSchema = z.object({ name: z.string().min(0, 'Name is required'), verify_jwt: z.boolean(), }) export const EdgeFunctionDetails = () => { const router = useRouter() const { ref: projectRef, functionSlug } = useParams() const showAllEdgeFunctionInvocationExamples = useIsFeatureEnabled( 'edge_functions:show_all_edge_function_invocation_examples' ) const invocationTabs = useMemo(() => { if (showAllEdgeFunctionInvocationExamples) return INVOCATION_TABS return INVOCATION_TABS.filter((tab) => tab.id === 'curl' || tab.id === 'supabase-js') }, [showAllEdgeFunctionInvocationExamples]) const [showKey, setShowKey] = useState(false) const [selectedTab, setSelectedTab] = useState(invocationTabs[0].id) const [showDeleteModal, setShowDeleteModal] = useState(false) const { can: canUpdateEdgeFunctionPermission } = useAsyncCheckPermissions( PermissionAction.FUNCTIONS_WRITE, '*' ) const canUpdateEdgeFunction = IS_PLATFORM && canUpdateEdgeFunctionPermission const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*') const { data: apiKeyData } = useAPIKeys({ projectRef }, { enabled: canReadAPIKeys }) const { anonKey, publishableKey } = apiKeyData ?? {} const { data: selectedFunction } = useEdgeFunctionQuery({ projectRef, slug: functionSlug }) const { data: endpoint } = useProjectApiUrl({ projectRef }) const functionUrl = `${endpoint}/functions/v1/${selectedFunction?.slug}` const { mutate: updateEdgeFunction, isPending: isUpdating } = useEdgeFunctionUpdateMutation() const { mutate: deleteEdgeFunction, isPending: isDeleting } = useEdgeFunctionDeleteMutation({ onSuccess: () => { toast.success(`Successfully deleted "${selectedFunction?.name}"`) router.push(`/project/${projectRef}/functions`) }, }) const form = useForm({ resolver: zodResolver(FormSchema), defaultValues: { name: '', verify_jwt: false }, }) const apiKey = publishableKey?.api_key ?? anonKey?.api_key ?? '[YOUR ANON KEY]' const { managementCommands } = generateCLICommands({ selectedFunction, functionUrl, anonKey: apiKey, }) const onUpdateFunction: SubmitHandler> = async (values: any) => { if (!projectRef) return console.error('Project ref is required') if (selectedFunction === undefined) return console.error('No edge function selected') updateEdgeFunction( { projectRef, slug: selectedFunction.slug, payload: values, }, { onSuccess: () => { toast.success(`Successfully updated edge function`) }, } ) } const onConfirmDelete = async () => { if (!projectRef) return console.error('Project ref is required') if (selectedFunction === undefined) return console.error('No edge function selected') deleteEdgeFunction({ projectRef, slug: selectedFunction.slug }) } useEffect(() => { if (selectedFunction) { form.reset({ name: selectedFunction.name, verify_jwt: selectedFunction.verify_jwt, }) } }, [selectedFunction]) return ( Function configuration
( )} /> {IS_PLATFORM && ( <> (

Requires a JWT signed{' '} only by the legacy secret {' '} in the{' '} Authorization {' '} header. The anon key satisfies this.

Recommended: OFF with JWT and custom auth logic in your function code.

} >
)} />
{form.formState.isDirty && ( )} )}
Invoke function {invocationTabs.map((tab) => ( {tab.label} ))} {selectedTab === 'curl' && ( )} {invocationTabs.map((tab) => { const code = tab.code({ showKey, functionUrl, functionName: selectedFunction?.name ?? '', apiKey, }) return ( code]:break-all' : '[&>code]:wrap-break-word' )} language={tab.language} wrapLines={false} hideLineNumbers={tab.hideLineNumbers} handleCopy={() => { copyToClipboard( tab.code({ showKey: true, functionUrl, functionName: selectedFunction?.name ?? '', apiKey, }) ) }} /> ) })} {IS_PLATFORM && ( <> Develop locally
( <> supabase functions download{' '} {selectedFunction?.slug} ), comment: '1. Download the function', }, ]} />
Delete function Once your function is deleted, it can no longer be restored Make sure you have made a backup if you want to restore your edge function setShowDeleteModal(false)} onConfirm={onConfirmDelete} alert={{ base: { variant: 'destructive' }, title: 'This action cannot be undone', description: 'Ensure that you have made a backup if you want to restore your edge function', }} /> )}
) }