mirror of
https://github.com/supabase/supabase.git
synced 2026-07-03 15:54:23 +08:00
* Replace all usage of useProjectContext with useSelectedProjectQuery * Replace all usage of useSelectedProject with useSelectedProjectQuery * Replace all usage of useProjectByRef with useProjectByRefQuery * Replace all usage of useSelectedOrganization with useSelectedOrganizationQuery * Deprecate useSelectedProject, useSelectedOrganization, and useProjectByRef hooks * Deprecate ProjecContext
221 lines
7.2 KiB
TypeScript
221 lines
7.2 KiB
TypeScript
import { Code } from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import { DragEvent, ReactNode, useState } from 'react'
|
|
import { toast } from 'sonner'
|
|
|
|
import { useParams } from 'common'
|
|
import { ReportBlockContainer } from 'components/interfaces/Reports/ReportBlock/ReportBlockContainer'
|
|
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
|
|
import { useEdgeFunctionQuery } from 'data/edge-functions/edge-function-query'
|
|
import { useEdgeFunctionDeployMutation } from 'data/edge-functions/edge-functions-deploy-mutation'
|
|
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
|
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
|
|
import { Button, cn, CodeBlock, CodeBlockLang } from 'ui'
|
|
import { Admonition } from 'ui-patterns'
|
|
|
|
interface EdgeFunctionBlockProps {
|
|
/** Title of the EdgeFunctionBlock */
|
|
label: string
|
|
/** Function code to display */
|
|
code: string
|
|
/** Function name/slug */
|
|
functionName: string
|
|
/** Any other actions specific to the parent to be rendered in the header */
|
|
actions?: ReactNode
|
|
/** Toggle visiblity of code on render */
|
|
showCode?: boolean
|
|
/** Whether function block is draggable */
|
|
draggable?: boolean
|
|
/** Tooltip when hovering over the header of the block */
|
|
tooltip?: ReactNode
|
|
/** Optional callback on drag start */
|
|
onDragStart?: (e: DragEvent<Element>) => void
|
|
}
|
|
|
|
export const EdgeFunctionBlock = ({
|
|
label,
|
|
code,
|
|
functionName,
|
|
actions,
|
|
showCode: _showCode = false,
|
|
tooltip,
|
|
}: EdgeFunctionBlockProps) => {
|
|
const { ref } = useParams()
|
|
const [isDeployed, setIsDeployed] = useState(false)
|
|
const [showWarning, setShowWarning] = useState(false)
|
|
const { data: settings } = useProjectSettingsV2Query({ projectRef: ref })
|
|
const { data: existingFunction } = useEdgeFunctionQuery({ projectRef: ref, slug: functionName })
|
|
|
|
const { mutate: sendEvent } = useSendEventMutation()
|
|
const { data: org } = useSelectedOrganizationQuery()
|
|
|
|
const { mutateAsync: deployFunction, isLoading: isDeploying } = useEdgeFunctionDeployMutation({
|
|
onSuccess: () => {
|
|
setIsDeployed(true)
|
|
toast.success('Successfully deployed edge function')
|
|
},
|
|
})
|
|
|
|
const handleDeploy = async () => {
|
|
if (!code || isDeploying || !ref) return
|
|
|
|
if (existingFunction) {
|
|
return setShowWarning(true)
|
|
}
|
|
|
|
try {
|
|
await deployFunction({
|
|
projectRef: ref,
|
|
slug: functionName,
|
|
metadata: {
|
|
entrypoint_path: 'index.ts',
|
|
name: functionName,
|
|
verify_jwt: true,
|
|
},
|
|
files: [{ name: 'index.ts', content: code }],
|
|
})
|
|
sendEvent({
|
|
action: 'edge_function_deploy_button_clicked',
|
|
properties: { origin: 'functions_ai_assistant' },
|
|
groups: { project: ref ?? 'Unknown', organization: org?.slug ?? 'Unknown' },
|
|
})
|
|
} catch (error) {
|
|
toast.error(
|
|
`Failed to deploy function: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
)
|
|
}
|
|
}
|
|
|
|
let functionUrl = 'Function URL not available'
|
|
const endpoint = settings?.app_config?.endpoint
|
|
if (endpoint) {
|
|
const restUrl = `https://${endpoint}`
|
|
const restUrlTld = restUrl ? new URL(restUrl).hostname.split('.').pop() : 'co'
|
|
functionUrl =
|
|
ref && functionName && restUrlTld
|
|
? `https://${ref}.supabase.${restUrlTld}/functions/v1/${functionName}`
|
|
: 'Function URL will be available after deployment'
|
|
}
|
|
return (
|
|
<ReportBlockContainer
|
|
tooltip={tooltip}
|
|
icon={<Code size={16} strokeWidth={1.5} className="text-foreground-muted" />}
|
|
label={label}
|
|
actions={
|
|
ref && functionName ? (
|
|
<>
|
|
<Button
|
|
type="outline"
|
|
size="tiny"
|
|
loading={isDeploying}
|
|
disabled={!ref}
|
|
onClick={handleDeploy}
|
|
>
|
|
{isDeploying ? 'Deploying...' : 'Deploy'}
|
|
</Button>
|
|
|
|
{actions}
|
|
</>
|
|
) : null
|
|
}
|
|
>
|
|
{showWarning && ref && functionName && (
|
|
<Admonition
|
|
type="warning"
|
|
className="mb-0 rounded-none border-0 border-b shrink-0 bg-background-100"
|
|
>
|
|
<p>An edge function with the name "{functionName}" already exists.</p>
|
|
<p className="text-foreground-light">
|
|
Deploying will replace the existing function. Are you sure you want to proceed?
|
|
</p>
|
|
<div className="flex justify-stretch mt-2 gap-2">
|
|
<Button
|
|
type="outline"
|
|
size="tiny"
|
|
className="w-full flex-1"
|
|
onClick={() => setShowWarning(false)}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
type="danger"
|
|
size="tiny"
|
|
className="w-full flex-1"
|
|
onClick={async () => {
|
|
setShowWarning(false)
|
|
try {
|
|
await deployFunction({
|
|
projectRef: ref,
|
|
slug: functionName,
|
|
metadata: {
|
|
entrypoint_path: 'index.ts',
|
|
name: functionName,
|
|
verify_jwt: true,
|
|
},
|
|
files: [{ name: 'index.ts', content: code }],
|
|
})
|
|
} catch (error) {
|
|
toast.error(
|
|
`Failed to deploy function: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
)
|
|
}
|
|
}}
|
|
>
|
|
Replace function
|
|
</Button>
|
|
</div>
|
|
</Admonition>
|
|
)}
|
|
|
|
<div className="shrink-0 w-full max-h-96 overflow-y-auto">
|
|
<CodeBlock
|
|
hideLineNumbers
|
|
wrapLines={false}
|
|
value={code}
|
|
language={'typescript' as CodeBlockLang}
|
|
className={cn(
|
|
'max-w-none block !bg-transparent !py-3 !px-3.5 prose dark:prose-dark border-0 text-foreground !rounded-none w-full',
|
|
'[&>code]:m-0 [&>code>span]:text-foreground'
|
|
)}
|
|
/>
|
|
</div>
|
|
|
|
{(isDeploying || isDeployed) && (
|
|
<div className="p-4 w-full border-t bg-surface-75 text-xs">
|
|
{isDeploying ? (
|
|
<p className="text-foreground-light">Deploying function...</p>
|
|
) : (
|
|
<>
|
|
<p className="text-foreground-light mb-2">
|
|
The{' '}
|
|
<Link
|
|
className="text-foreground"
|
|
href={`/project/${ref}/functions/${functionName}/details`}
|
|
>
|
|
new function
|
|
</Link>{' '}
|
|
is now live at:
|
|
</p>
|
|
<CodeBlock
|
|
language="bash"
|
|
hideLineNumbers
|
|
value={functionUrl}
|
|
className="text-xs p-2"
|
|
/>
|
|
<p className="text-foreground-light mt-4 mb-2">
|
|
To download and work on this function locally, use the CLI command:
|
|
</p>
|
|
<CodeBlock
|
|
hideLineNumbers
|
|
language="bash"
|
|
value={`supabase functions download ${functionName}`}
|
|
className="text-xs p-2"
|
|
/>
|
|
</>
|
|
)}
|
|
</div>
|
|
)}
|
|
</ReportBlockContainer>
|
|
)
|
|
}
|