mirror of
https://github.com/supabase/supabase.git
synced 2026-10-17 07:09:07 +03:00
Rename secrets to env vars, add inline form
This commit is contained in:
1 parent
5283db12a3
commit
ecfb6284fe
5 files changed
+153
-34
No files matched your search
+4
-4
@@ -37,7 +37,7 @@ const EdgeFunctionDetails = () => {
|
||||
const router = useRouter()
|
||||
const { ref: projectRef, functionSlug } = useParams()
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
const [showInstructions, setShowInstructions] = useState(false)
|
||||
const [showInstructions, setShowInstructions] = useState(true)
|
||||
|
||||
const { data: settings } = useProjectSettingsV2Query({ projectRef })
|
||||
const { data: customDomainData } = useCustomDomainsQuery({ projectRef })
|
||||
@@ -150,7 +150,7 @@ const EdgeFunctionDetails = () => {
|
||||
value={selectedFunction?.slug}
|
||||
/>
|
||||
<Input disabled copy label="Endpoint URL" value={functionUrl} />
|
||||
<Input disabled label="Region" value="All functions are deployed globally" />
|
||||
{/* <Input disabled label="Region" value="All functions are deployed globally" /> */}
|
||||
<Input
|
||||
disabled
|
||||
label="Created at"
|
||||
@@ -220,7 +220,7 @@ const EdgeFunctionDetails = () => {
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded border bg-foreground p-2 text-background">
|
||||
<Terminal size={18} strokeWidth={2} />
|
||||
</div>
|
||||
<h4>Command line access</h4>
|
||||
<h4>Deploy, invoke and manage environment variables</h4>
|
||||
</div>
|
||||
<div className="cursor-pointer" onClick={() => setShowInstructions(!showInstructions)}>
|
||||
{showInstructions ? (
|
||||
@@ -235,7 +235,7 @@ const EdgeFunctionDetails = () => {
|
||||
<CommandRender commands={managementCommands} />
|
||||
<h5 className="text-base">Invoke </h5>
|
||||
<CommandRender commands={invokeCommands} />
|
||||
<h5 className="text-base">Secrets management</h5>
|
||||
<h5 className="text-base">Environment variables (secrets)</h5>
|
||||
<CommandRender commands={secretCommands} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { SubmitHandler, useForm } from 'react-hook-form'
|
||||
import { toast } from 'sonner'
|
||||
import z from 'zod'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { useSecretsCreateMutation } from 'data/secrets/secrets-create-mutation'
|
||||
import { Eye, EyeOff } from 'lucide-react'
|
||||
import {
|
||||
Button,
|
||||
Form_Shadcn_,
|
||||
FormControl_Shadcn_,
|
||||
FormField_Shadcn_,
|
||||
FormItem_Shadcn_,
|
||||
FormLabel_Shadcn_,
|
||||
} from 'ui'
|
||||
import { Input } from 'ui-patterns/DataInputs/Input'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
import Panel from 'components/ui/Panel'
|
||||
|
||||
interface AddNewSecretFormProps {
|
||||
onComplete?: () => void
|
||||
}
|
||||
|
||||
const AddNewSecretForm = ({ onComplete }: AddNewSecretFormProps) => {
|
||||
const { ref: projectRef } = useParams()
|
||||
const submitRef = useRef<HTMLButtonElement>(null)
|
||||
const [showSecretValue, setShowSecretValue] = useState(false)
|
||||
|
||||
const FormSchema = z.object({
|
||||
name: z
|
||||
.string()
|
||||
.min(1, 'Please provide a name for your secret')
|
||||
.refine((value) => !value.match(/^(SUPABASE_).*/), {
|
||||
message: 'Name must not start with the SUPABASE_ prefix',
|
||||
}),
|
||||
value: z.string().min(1, 'Please provider a value for your secret'),
|
||||
})
|
||||
const defaultValues = { name: '', value: '' }
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues,
|
||||
})
|
||||
|
||||
const { mutate: createSecret, isLoading: isCreating } = useSecretsCreateMutation({
|
||||
onSuccess: (_, variables) => {
|
||||
toast.success(`Successfully created new secret "${variables.secrets[0].name}"`)
|
||||
form.reset(defaultValues)
|
||||
onComplete?.()
|
||||
},
|
||||
})
|
||||
|
||||
const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = async (data) => {
|
||||
createSecret({ projectRef, secrets: [data] })
|
||||
}
|
||||
|
||||
return (
|
||||
<Panel>
|
||||
<Panel.Content className="grid gap-4">
|
||||
<h2 className="text-sm">Add a new environment variable</h2>
|
||||
<Form_Shadcn_ {...form}>
|
||||
<form
|
||||
id="create-secret-form"
|
||||
className="w-full grid grid-cols-2 gap-4"
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormLabel_Shadcn_>Key</FormLabel_Shadcn_>
|
||||
<FormControl_Shadcn_>
|
||||
<Input {...field} placeholder="e.g. CLIENT_KEY" />
|
||||
</FormControl_Shadcn_>
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="value"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormLabel_Shadcn_>Value</FormLabel_Shadcn_>
|
||||
<FormControl_Shadcn_>
|
||||
<Input
|
||||
{...field}
|
||||
type={showSecretValue ? 'text' : 'password'}
|
||||
actions={
|
||||
<div className="mr-1">
|
||||
<Button
|
||||
type="default"
|
||||
className="px-1"
|
||||
icon={showSecretValue ? <EyeOff /> : <Eye />}
|
||||
onClick={() => setShowSecretValue(!showSecretValue)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center gap-2 col-span-2">
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={isCreating}
|
||||
loading={isCreating}
|
||||
onClick={() => submitRef?.current?.click()}
|
||||
>
|
||||
{isCreating ? 'Saving...' : 'Save'}
|
||||
</Button>
|
||||
</div>
|
||||
<button className="hidden" type="submit" ref={submitRef} />
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
</Panel.Content>
|
||||
</Panel>
|
||||
)
|
||||
}
|
||||
|
||||
export default AddNewSecretForm
|
||||
+11
-24
@@ -13,11 +13,11 @@ import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useSecretsDeleteMutation } from 'data/secrets/secrets-delete-mutation'
|
||||
import { ProjectSecret, useSecretsQuery } from 'data/secrets/secrets-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { Badge } from 'ui'
|
||||
import { Badge, Separator } from 'ui'
|
||||
import { Input } from 'ui-patterns/DataInputs/Input'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import AddNewSecretModal from './AddNewSecretModal'
|
||||
import EdgeFunctionSecret from './EdgeFunctionSecret'
|
||||
import AddNewSecretForm from './AddNewSecretForm'
|
||||
|
||||
const EdgeFunctionSecrets = () => {
|
||||
const { ref: projectRef } = useParams()
|
||||
@@ -48,11 +48,17 @@ const EdgeFunctionSecrets = () => {
|
||||
return (
|
||||
<>
|
||||
{isLoading && <GenericSkeletonLoader />}
|
||||
|
||||
{isError && <AlertError error={error} subject="Failed to retrieve project secrets" />}
|
||||
|
||||
{canUpdateSecrets && (
|
||||
<div className="grid gap-5">
|
||||
<AddNewSecretForm onComplete={() => setShowCreateSecret(false)} />
|
||||
<Separator />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isSuccess && (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-4 mt-4">
|
||||
{!canReadSecrets ? (
|
||||
<NoPermission resourceText="view this project's edge function secrets" />
|
||||
) : (
|
||||
@@ -61,28 +67,11 @@ const EdgeFunctionSecrets = () => {
|
||||
<Input
|
||||
size="small"
|
||||
className="w-80"
|
||||
placeholder="Search for a secret"
|
||||
placeholder="Search..."
|
||||
value={searchString}
|
||||
onChange={(e: any) => setSearchString(e.target.value)}
|
||||
icon={<Search size={14} />}
|
||||
/>
|
||||
<div className="flex items-center space-x-2">
|
||||
<DocsButton href="https://supabase.com/docs/guides/functions/secrets" />
|
||||
<ButtonTooltip
|
||||
disabled={!canUpdateSecrets}
|
||||
onClick={() => setShowCreateSecret(true)}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
text: !canUpdateSecrets
|
||||
? 'You need additional permissions to update edge function secrets'
|
||||
: undefined,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Add new secret
|
||||
</ButtonTooltip>
|
||||
</div>
|
||||
</div>
|
||||
<Table
|
||||
head={[
|
||||
@@ -130,8 +119,6 @@ const EdgeFunctionSecrets = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<AddNewSecretModal visible={showCreateSecret} onClose={() => setShowCreateSecret(false)} />
|
||||
|
||||
<ConfirmationModal
|
||||
variant="warning"
|
||||
loading={isDeleting}
|
||||
|
||||
@@ -111,7 +111,9 @@ const FunctionsLayout = ({ title, children }: PropsWithChildren<FunctionsLayoutP
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button asChild type="default">
|
||||
<Link href={`/project/${ref}/settings/functions`}>Manage secrets</Link>
|
||||
<Link href={`/project/${ref}/settings/functions`}>
|
||||
Manage environment variables
|
||||
</Link>
|
||||
</Button>
|
||||
{isNewAPIDocsEnabled && (
|
||||
<APIDocsButton
|
||||
|
||||
@@ -7,16 +7,22 @@ import {
|
||||
ScaffoldTitle,
|
||||
} from 'components/layouts/Scaffold'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
|
||||
const PageLayout: NextPageWithLayout = () => {
|
||||
return (
|
||||
<>
|
||||
<ScaffoldContainer>
|
||||
<ScaffoldHeader>
|
||||
<ScaffoldTitle>Edge Function Secrets Management</ScaffoldTitle>
|
||||
<ScaffoldDescription>
|
||||
Manage the secrets for your project's edge functions
|
||||
</ScaffoldDescription>
|
||||
<ScaffoldHeader className="flex flex-row justify-between">
|
||||
<div>
|
||||
<ScaffoldTitle>Edge Function Environment Variables</ScaffoldTitle>
|
||||
<ScaffoldDescription>
|
||||
Manage the environment variables (secrets) for your project's edge functions
|
||||
</ScaffoldDescription>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<DocsButton href="https://supabase.com/docs/guides/functions/secrets" />
|
||||
</div>
|
||||
</ScaffoldHeader>
|
||||
</ScaffoldContainer>
|
||||
<ScaffoldContainer bottomPadding>
|
||||
|
||||
Reference in new issue
Block a user