import { useParams } from 'common' import { ChevronDown } from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import { toast } from 'sonner' import { Button, Collapsible, Form, Input, Listbox, Modal, Toggle, cn } from 'ui' import { StorageSizeUnits } from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.constants' import { convertFromBytes, convertToBytes, } from 'components/to-be-cleaned/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 { IS_PLATFORM } from 'lib/constants' import { Admonition } from 'ui-patterns' import type { StorageBucket } from './Storage.types' export interface EditBucketModalProps { visible: boolean bucket?: StorageBucket onClose: () => void } const EditBucketModal = ({ visible, bucket, onClose }: EditBucketModalProps) => { const { ref } = useParams() const { mutate: updateBucket, isLoading: isUpdating } = useBucketUpdateMutation({ onSuccess: () => { toast.success(`Successfully updated bucket "${bucket?.name}"`) onClose() }, }) const { data } = useProjectStorageConfigQuery( { projectRef: ref }, { enabled: IS_PLATFORM && visible } ) const { value, unit } = convertFromBytes(data?.fileSizeLimit ?? 0) const formattedGlobalUploadLimit = `${value} ${unit}` const [selectedUnit, setSelectedUnit] = useState(StorageSizeUnits.BYTES) const [showConfiguration, setShowConfiguration] = useState(false) const validate = (values: any) => { const errors = {} as any if (values.has_file_size_limit && values.formatted_size_limit < 0) { errors.formatted_size_limit = 'File size upload limit has to be at least 0' } return errors } const onSubmit = async (values: any) => { 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) : null, allowed_mime_types: values.allowed_mime_types.length > 0 ? values.allowed_mime_types.split(',').map((x: string) => x.trim()) : null, }) } useEffect(() => { if (visible) { const { unit } = convertFromBytes(bucket?.file_size_limit ?? 0) setSelectedUnit(unit) setShowConfiguration(false) } }, [visible]) return (
{({ values, resetForm }: { values: any; resetForm: any }) => { const isChangingBucketVisibility = bucket?.public !== values.public const isMakingBucketPrivate = bucket?.public && !values.public const isMakingBucketPublic = !bucket?.public && values.public // [Alaister] although this "technically" is breaking the rules of React hooks // it won't error because the hooks are always rendered in the same order // eslint-disable-next-line react-hooks/rules-of-hooks useEffect(() => { if (visible && bucket !== undefined) { const { value: fileSizeLimit } = convertFromBytes(bucket.file_size_limit ?? 0) const values = { name: bucket.name ?? '', public: bucket.public, file_size_limit: bucket.file_size_limit, allowed_mime_types: (bucket.allowed_mime_types ?? []).join(', '), has_file_size_limit: bucket.file_size_limit !== null, formatted_size_limit: fileSizeLimit ?? 0, } resetForm({ values, initialValues: values }) } }, [visible]) return ( <>
{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.` : ''}

{isMakingBucketPrivate && (

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)} >

Additional configuration

{values.has_file_size_limit && (
{ if (event.charCode < 48 || event.charCode > 57) { event.preventDefault() } }} descriptionText={`Equivalent to ${convertToBytes( values.formatted_size_limit, selectedUnit ).toLocaleString()} bytes.`} />
{Object.values(StorageSizeUnits).map((unit: string) => (
{unit}
))}
{IS_PLATFORM && (

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

)}
)}
) }}
) } export default EditBucketModal