import { zodResolver } from '@hookform/resolvers/zod' import { useParams } from 'common' import { ChevronDown } from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import { type SubmitHandler, useForm } from 'react-hook-form' import { toast } from 'sonner' import { Button, CollapsibleContent_Shadcn_, CollapsibleTrigger_Shadcn_, Collapsible_Shadcn_, Dialog, DialogContent, DialogFooter, DialogHeader, DialogSection, DialogSectionSeparator, DialogTitle, FormControl_Shadcn_, FormField_Shadcn_, Form_Shadcn_, Input_Shadcn_, SelectContent_Shadcn_, SelectItem_Shadcn_, SelectTrigger_Shadcn_, SelectValue_Shadcn_, Select_Shadcn_, Switch, cn, } from 'ui' import { z } from 'zod' import { StorageSizeUnits } from 'components/interfaces/Storage/StorageSettings/StorageSettings.constants' import { convertFromBytes, convertToBytes, } from 'components/interfaces/Storage/StorageSettings/StorageSettings.utils' import { InlineLink } from 'components/ui/InlineLink' import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query' import { useBucketUpdateMutation } from 'data/storage/bucket-update-mutation' import { Bucket } from 'data/storage/buckets-query' import { IS_PLATFORM } from 'lib/constants' import { isNonNullable } from 'lib/isNonNullable' import { Admonition } from 'ui-patterns' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' export interface EditBucketModalProps { visible: boolean bucket: Bucket onClose: () => void } const BucketSchema = z.object({ name: z.string(), public: z.boolean().default(false), has_file_size_limit: z.boolean().default(false), formatted_size_limit: z.coerce .number() .min(0, 'File size upload limit has to be at least 0') .default(0), allowed_mime_types: z.string().trim().default(''), }) const formId = 'edit-storage-bucket-form' export const EditBucketModal = ({ visible, bucket, onClose }: EditBucketModalProps) => { const { ref } = useParams() const { mutate: updateBucket, isLoading: isUpdating } = useBucketUpdateMutation() const { data } = useProjectStorageConfigQuery({ projectRef: ref }, { enabled: IS_PLATFORM }) const { value, unit } = convertFromBytes(data?.fileSizeLimit ?? 0) const formattedGlobalUploadLimit = `${value} ${unit}` const [selectedUnit, setSelectedUnit] = useState(StorageSizeUnits.BYTES) const [showConfiguration, setShowConfiguration] = useState(false) const { value: fileSizeLimit } = convertFromBytes(bucket?.file_size_limit ?? 0) const form = useForm>({ resolver: zodResolver(BucketSchema), defaultValues: { name: bucket?.name ?? '', public: bucket?.public, has_file_size_limit: isNonNullable(bucket?.file_size_limit), formatted_size_limit: fileSizeLimit ?? 0, allowed_mime_types: (bucket?.allowed_mime_types ?? []).join(', '), }, values: { name: bucket?.name ?? '', public: bucket?.public, has_file_size_limit: isNonNullable(bucket?.file_size_limit), formatted_size_limit: fileSizeLimit ?? 0, allowed_mime_types: (bucket?.allowed_mime_types ?? []).join(', '), }, mode: 'onSubmit', }) const isPublicBucket = form.watch('public') const hasFileSizeLimit = form.watch('has_file_size_limit') const formattedSizeLimit = form.watch('formatted_size_limit') const isChangingBucketVisibility = bucket?.public !== isPublicBucket const isMakingBucketPrivate = bucket?.public && !isPublicBucket const isMakingBucketPublic = !bucket?.public && isPublicBucket const onSubmit: SubmitHandler> = async (values) => { if (bucket === undefined) return console.error('Bucket is required') if (ref === undefined) return console.error('Project ref is required') updateBucket( { projectRef: ref, id: bucket.id, isPublic: values.public, file_size_limit: values.has_file_size_limit ? convertToBytes(values.formatted_size_limit, selectedUnit as StorageSizeUnits) : null, allowed_mime_types: values.allowed_mime_types.length > 0 ? values.allowed_mime_types.split(',').map((x: string) => x.trim()) : null, }, { onSuccess: () => { toast.success(`Successfully updated bucket "${bucket?.name}"`) onClose() }, } ) } useEffect(() => { if (visible && bucket) { setShowConfiguration(false) const { unit } = convertFromBytes(bucket.file_size_limit ?? 0) setSelectedUnit(unit) } }, [visible, bucket]) return ( { if (!open) { form.reset() onClose() } }} > {`Edit bucket "${bucket?.name}"`}
( )} /> ( )} /> {isChangingBucketVisibility && ( {isMakingBucketPublic && (

This will make all objects in your bucket publicly accessible.

)} {isMakingBucketPrivate && ( <>

All objects in your bucket will be private and only accessible via signed URLs, or downloaded with the right authorisation headers.

Assets cached in the CDN may still be publicly accessible. You can consider{' '} purging the cache {' '} or moving your assets to a new bucket.

)} } /> )} setShowConfiguration(!showConfiguration)} >
( )} /> {hasFileSizeLimit && (
( )} />
<>{selectedUnit} {Object.values(StorageSizeUnits).map((unit: string) => (
{unit}
))}
{IS_PLATFORM && (

Note: Individual bucket upload will still be capped at the{' '} global upload limit {' '} of {formattedGlobalUploadLimit}

)}
)}
( )} />
) }