import { PermissionAction } from '@supabase/shared-types/out/constants' import { Download, FileArchive, Send } from 'lucide-react' import { useRouter } from 'next/router' import React, { useEffect, useState, type PropsWithChildren } from 'react' import { toast } from 'sonner' import { BlobReader, BlobWriter, ZipWriter } from '@zip.js/zip.js' import { useParams } from 'common' import { useIsAPIDocsSidePanelEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { EdgeFunctionTesterSheet } from 'components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionTesterSheet' import { APIDocsButton } from 'components/ui/APIDocsButton' import { DocsButton } from 'components/ui/DocsButton' import NoPermission from 'components/ui/NoPermission' import { useEdgeFunctionBodyQuery } from 'data/edge-functions/edge-function-body-query' import { useEdgeFunctionQuery } from 'data/edge-functions/edge-function-query' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { withAuth } from 'hooks/misc/withAuth' import { DOCS_URL } from 'lib/constants' import Link from 'next/link' import { BreadcrumbItem_Shadcn_ as BreadcrumbItem, BreadcrumbLink_Shadcn_ as BreadcrumbLink, BreadcrumbList_Shadcn_ as BreadcrumbList, BreadcrumbSeparator_Shadcn_ as BreadcrumbSeparator, Button, NavMenu, NavMenuItem, Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Separator, } from 'ui' import { Input } from 'ui-patterns/DataInputs/Input' import { PageHeader, PageHeaderAside, PageHeaderBreadcrumb, PageHeaderMeta, PageHeaderNavigationTabs, PageHeaderSummary, PageHeaderTitle, } from 'ui-patterns/PageHeader' import { ProjectLayout } from '../ProjectLayout' import EdgeFunctionsLayout from './EdgeFunctionsLayout' interface EdgeFunctionDetailsLayoutProps { title?: string } const EdgeFunctionDetailsLayout = ({ title, children, }: PropsWithChildren) => { const router = useRouter() const { data: org } = useSelectedOrganizationQuery() const { functionSlug, ref } = useParams() const { mutate: sendEvent } = useSendEventMutation() const isNewAPIDocsEnabled = useIsAPIDocsSidePanelEnabled() const { isLoading, can: canReadFunctions } = useAsyncCheckPermissions( PermissionAction.FUNCTIONS_READ, '*' ) const [isOpen, setIsOpen] = useState(false) const { data: selectedFunction, error, isError, } = useEdgeFunctionQuery({ projectRef: ref, slug: functionSlug }) const { data: functionBody = { version: 0, files: [] }, error: filesError } = useEdgeFunctionBodyQuery( { projectRef: ref, slug: functionSlug, }, { retry: false, retryOnMount: true, refetchOnWindowFocus: false, staleTime: Infinity, refetchOnMount: false, refetchOnReconnect: false, refetchInterval: false, refetchIntervalInBackground: false, } ) const name = selectedFunction?.name || '' const breadcrumbItems = [ { label: 'Edge Functions', href: `/project/${ref}/functions`, }, { label: functionSlug, href: `/project/${ref}/functions/${functionSlug}`, }, ] const navigationItems = functionSlug ? [ { label: 'Overview', href: `/project/${ref}/functions/${functionSlug}`, }, { label: 'Invocations', href: `/project/${ref}/functions/${functionSlug}/invocations`, }, { label: 'Logs', href: `/project/${ref}/functions/${functionSlug}/logs`, }, { label: 'Code', href: `/project/${ref}/functions/${functionSlug}/code`, }, { label: 'Details', href: `/project/${ref}/functions/${functionSlug}/details`, }, ] : [] const downloadFunction = async () => { if (filesError) return toast.error('Failed to retrieve edge function files') const zipFileWriter = new BlobWriter('application/zip') const zipWriter = new ZipWriter(zipFileWriter, { bufferedWrite: true }) functionBody.files.forEach((file) => { const nameSections = file.name.split('/') const slugIndex = nameSections.indexOf(functionSlug ?? '') const fileName = nameSections.slice(slugIndex + 1).join('/') const fileBlob = new Blob([file.content]) zipWriter.add(fileName, new BlobReader(fileBlob)) }) const blobURL = URL.createObjectURL(await zipWriter.close()) const link = document.createElement('a') link.href = blobURL link.setAttribute('download', `${functionSlug}.zip`) document.body.appendChild(link) link.click() link.parentNode?.removeChild(link) } useEffect(() => { let cancel = false if (!!functionSlug && isError && error.code === 404 && !cancel) { toast('Edge function cannot be found in your project') router.push(`/project/${ref}/functions`) } return () => { cancel = true } }, [isError]) if (!isLoading && !canReadFunctions) { return ( ) } return (
{breadcrumbItems.length > 0 && ( {breadcrumbItems.map((item, index) => ( {item.href ? ( {item.label} ) : ( {item.label} )} {index < breadcrumbItems.length - 1 && } ))} )} {functionSlug ? name : 'Edge Functions'}
{isNewAPIDocsEnabled && ( )}

Download via CLI

{!!functionSlug && ( )}
{navigationItems.length > 0 && ( {navigationItems.map((item) => { const isActive = router.asPath.split('?')[0] === item.href return ( {item.label} ) })} )}
{children} setIsOpen(false)} />
) } export default withAuth(EdgeFunctionDetailsLayout)