Revert "Chore/toggle for storage client connection s3 (#31209)"

This reverts commit e00ba41fd1.
This commit is contained in:
Joshen Lim authored and GitHub committed 2025-01-06 11:01:56 +07:00
1 parent e00ba41fd1
commit 57318c6060
8 files changed
+73 -218

No files matched your search

@@ -27,8 +27,6 @@ import {
} from 'ui'
import { Input } from 'ui-patterns/DataInputs/Input'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
interface CreateCredentialModalProps {
visible: boolean
@@ -42,10 +40,6 @@ export const CreateCredentialModal = ({ visible, onOpenChange }: CreateCredentia
const canCreateCredentials = useCheckPermissions(PermissionAction.STORAGE_ADMIN_WRITE, '*')
const { data: config } = useProjectStorageConfigQuery({ projectRef })
const isS3ConnectionEnabled = config?.features.s3Protocol.enabled
const disableCreation = !isProjectActive || !canCreateCredentials || !isS3ConnectionEnabled
const FormSchema = z.object({
description: z.string().min(3, {
message: 'Description must be at least 3 characters long',
@@ -84,20 +78,22 @@ export const CreateCredentialModal = ({ visible, onOpenChange }: CreateCredentia
<Tooltip_Shadcn_>
<TooltipTrigger_Shadcn_ asChild>
<DialogTrigger asChild>
<Button type="default" disabled={disableCreation} className="pointer-events-auto">
<Button
type="default"
disabled={!isProjectActive || !canCreateCredentials}
className="pointer-events-auto"
>
New access key
</Button>
</DialogTrigger>
</TooltipTrigger_Shadcn_>
{disableCreation && (
{(!isProjectActive || !canCreateCredentials) && (
<TooltipContent_Shadcn_ side="bottom">
{!isProjectActive
? 'Restore your project to create new access keys'
: !isS3ConnectionEnabled
? 'Connection via S3 protocol is currently disabled'
: !canCreateCredentials
? 'You need additional permissions to create new access keys'
: ''}
: !canCreateCredentials
? 'You need additional permissions to create new access keys'
: ''}
</TooltipContent_Shadcn_>
)}
</Tooltip_Shadcn_>
@@ -163,7 +159,7 @@ export const CreateCredentialModal = ({ visible, onOpenChange }: CreateCredentia
<DialogSection>
<FormField
name="description"
render={() => (
render={({ field }) => (
<FormItemLayout label="Description">
<Input
autoComplete="off"
@@ -1,11 +1,7 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { AlertTitle } from '@ui/components/shadcn/ui/alert'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { SubmitHandler, useForm } from 'react-hook-form'
import { toast } from 'sonner'
import * as z from 'zod'
import { useState } from 'react'
import { useParams } from 'common'
import {
@@ -13,26 +9,13 @@ import {
useProjectContext,
} from 'components/layouts/ProjectLayout/ProjectContext'
import Table from 'components/to-be-cleaned/Table'
import AlertError from 'components/ui/AlertError'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import NoPermission from 'components/ui/NoPermission'
import Panel from 'components/ui/Panel'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query'
import { useProjectStorageConfigUpdateUpdateMutation } from 'data/config/project-storage-config-update-mutation'
import { useStorageCredentialsQuery } from 'data/storage/s3-access-key-query'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import {
AlertDescription_Shadcn_,
Alert_Shadcn_,
Button,
FormControl_Shadcn_,
FormField_Shadcn_,
Form_Shadcn_,
Separator,
Switch,
WarningIcon,
} from 'ui'
import { AlertDescription_Shadcn_, Alert_Shadcn_, Button, WarningIcon } from 'ui'
import { Input } from 'ui-patterns/DataInputs/Input'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
@@ -51,169 +34,54 @@ export const S3Connection = () => {
const [deleteCred, setDeleteCred] = useState<{ id: string; description: string }>()
const canReadS3Credentials = useCheckPermissions(PermissionAction.STORAGE_ADMIN_READ, '*')
const canUpdateStorageSettings = useCheckPermissions(PermissionAction.STORAGE_ADMIN_WRITE, '*')
const { data: settings } = useProjectSettingsV2Query({ projectRef })
const {
data: config,
error: configError,
isSuccess: isSuccessStorageConfig,
isError: isErrorStorageConfig,
} = useProjectStorageConfigQuery({ projectRef })
const { data: storageCreds, isLoading: isLoadingStorageCreds } = useStorageCredentialsQuery(
const { data: storageCreds, ...storageCredsQuery } = useStorageCredentialsQuery(
{ projectRef },
{ enabled: canReadS3Credentials }
)
const { mutate: updateStorageConfig, isLoading: isUpdating } =
useProjectStorageConfigUpdateUpdateMutation({
onSuccess: (_, vars) => {
if (vars.features) form.reset({ s3ConnectionEnabled: vars.features.s3Protocol.enabled })
toast.success('Successfully updated storage settings')
},
})
const FormSchema = z.object({ s3ConnectionEnabled: z.boolean() })
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: { s3ConnectionEnabled: false },
})
const protocol = settings?.app_config?.protocol ?? 'https'
const endpoint = settings?.app_config?.endpoint
const hasStorageCreds = storageCreds?.data && storageCreds.data.length > 0
const s3connectionUrl = getConnectionURL(projectRef ?? '', protocol, endpoint)
const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = async (data) => {
if (!projectRef) return console.error('Project ref is required')
if (!config) return console.error('Storage config is required')
updateStorageConfig({
projectRef,
features: {
...config?.features,
s3Protocol: { enabled: data.s3ConnectionEnabled },
},
})
}
useEffect(() => {
form.reset({ s3ConnectionEnabled: config?.features.s3Protocol.enabled })
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isSuccessStorageConfig])
return (
<>
<Form_Shadcn_ {...form}>
<form id="s3-connection-form" onSubmit={form.handleSubmit(onSubmit)}>
<FormHeader
title="S3 Connection"
description="Connect to your bucket using any S3-compatible service via the S3 protocol"
docsUrl="https://supabase.com/docs/guides/storage/s3/authentication"
/>
{projectIsLoading ? (
<GenericSkeletonLoader />
) : isProjectActive ? (
<Panel className="!mb-0">
<FormField_Shadcn_
name="s3ConnectionEnabled"
control={form.control}
render={({ field }) => (
<FormItemLayout
layout="horizontal"
className="px-8 py-8 [&>*>label]:text-foreground"
label="Enable connection via S3 protocol"
description="Allow clients to connect to Supabase Storage via the S3 protocol"
>
<FormControl_Shadcn_>
<Switch
size="large"
checked={field.value}
onCheckedChange={field.onChange}
disabled={!isSuccessStorageConfig || field.disabled}
/>
</FormControl_Shadcn_>
</FormItemLayout>
)}
/>
{isErrorStorageConfig && (
<div className="px-8 pb-8">
<AlertError
subject="Failed to retrieve storage configuration"
error={configError}
/>
</div>
)}
<Separator className="bg-border" />
<div className="flex flex-col gap-y-4 py-8">
<FormItemLayout
layout="horizontal"
className="px-8"
label="Endpoint"
isReactForm={false}
>
<Input readOnly copy disabled value={s3connectionUrl} />
</FormItemLayout>
{!projectIsLoading && (
<FormItemLayout
layout="horizontal"
className="px-8"
label="Region"
isReactForm={false}
>
<Input className="input-mono" copy disabled value={project?.region} />
</FormItemLayout>
)}
</div>
<Separator />
<div className="flex items-center justify-between w-full gap-2 px-8 py-4">
{!canUpdateStorageSettings ? (
<p className="text-sm text-foreground-light">
You need additional permissions to update storage settings
</p>
) : (
<div />
)}
<div className="flex gap-2">
<Button
type="default"
htmlType="reset"
onClick={() => form.reset()}
disabled={!form.formState.isDirty || !canUpdateStorageSettings || isUpdating}
>
Cancel
</Button>
<Button
type="primary"
htmlType="submit"
loading={isUpdating}
disabled={!form.formState.isDirty || !canUpdateStorageSettings || isUpdating}
>
Save
</Button>
</div>
</div>
</Panel>
) : (
<Alert_Shadcn_ variant="warning">
<WarningIcon />
<AlertTitle>Project is paused</AlertTitle>
<AlertDescription_Shadcn_>
To connect to your S3 bucket, you need to restore your project.
</AlertDescription_Shadcn_>
<div className="mt-3 flex items-center space-x-2">
<Button asChild type="default">
<Link href={`/project/${projectRef}`}>Restore project</Link>
</Button>
</div>
</Alert_Shadcn_>
)}
</form>
</Form_Shadcn_>
<div>
<FormHeader
title="S3 Connection"
description="Connect to your bucket via the S3 protocol."
docsUrl="https://supabase.com/docs/guides/storage/s3/authentication"
/>
{projectIsLoading ? (
<GenericSkeletonLoader />
) : isProjectActive ? (
<Panel className="grid gap-4 p-4 !mb-0">
<FormItemLayout layout="horizontal" label="Endpoint" isReactForm={false}>
<Input readOnly copy disabled value={s3connectionUrl} />
</FormItemLayout>
{!projectIsLoading && (
<FormItemLayout layout="horizontal" label="Region" isReactForm={false}>
<Input className="input-mono" copy disabled value={project?.region} />
</FormItemLayout>
)}
</Panel>
) : (
<Alert_Shadcn_ variant="warning">
<WarningIcon />
<AlertTitle>Project is paused</AlertTitle>
<AlertDescription_Shadcn_>
To connect to your S3 bucket, you need to restore your project.
</AlertDescription_Shadcn_>
<div className="mt-3 flex items-center space-x-2">
<Button asChild type="default">
<Link href={`/project/${projectRef}`}>Restore project</Link>
</Button>
</div>
</Alert_Shadcn_>
)}
</div>
<div>
<FormHeader
@@ -243,7 +111,7 @@ export const S3Connection = () => {
</Alert_Shadcn_>
) : (
<>
{isLoadingStorageCreds ? (
{storageCredsQuery.isLoading ? (
<div className="p-4">
<GenericSkeletonLoader />
</div>
@@ -8,7 +8,6 @@ import * as z from 'zod'
import { useParams } from 'common'
import AlertError from 'components/ui/AlertError'
import { InlineLink } from 'components/ui/InlineLink'
import NoPermission from 'components/ui/NoPermission'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import UpgradeToPro from 'components/ui/UpgradeToPro'
@@ -36,6 +35,7 @@ import {
} from 'ui'
import { STORAGE_FILE_SIZE_LIMIT_MAX_BYTES, StorageSizeUnits } from './StorageSettings.constants'
import { convertFromBytes, convertToBytes } from './StorageSettings.utils'
import { Markdown } from 'components/interfaces/Markdown'
interface StorageSettingsState {
fileSizeLimit: number
@@ -54,7 +54,7 @@ const StorageSettings = () => {
isLoading,
isSuccess,
isError,
} = useProjectStorageConfigQuery({ projectRef })
} = useProjectStorageConfigQuery({ projectRef }, { enabled: IS_PLATFORM })
const organization = useSelectedOrganization()
const { data: subscription, isSuccess: isSuccessSubscription } = useOrgSubscriptionQuery({
@@ -116,7 +116,7 @@ const StorageSettings = () => {
resolver: zodResolver(FormSchema),
defaultValues: initialValues,
})
const { fileSizeLimit: limit, unit: storageUnit } = form.watch()
const { fileSizeLimit: limit, unit: storageUnit, imageTransformationEnabled } = form.watch()
const { mutate: updateStorageConfig, isLoading: isUpdating } =
useProjectStorageConfigUpdateUpdateMutation({
@@ -127,14 +127,13 @@ const StorageSettings = () => {
const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = async (data) => {
if (!projectRef) return console.error('Project ref is required')
if (!config) return console.error('Storage config is required')
updateStorageConfig({
projectRef,
fileSizeLimit: convertToBytes(data.fileSizeLimit, data.unit),
features: {
imageTransformation: { enabled: data.imageTransformationEnabled },
s3Protocol: config.features.s3Protocol,
imageTransformation: {
enabled: data.imageTransformationEnabled,
},
},
})
}
@@ -154,7 +153,7 @@ const StorageSettings = () => {
{isSuccess && (
<form id={formId} className="" onSubmit={form.handleSubmit(onSubmit)}>
<div className="bg-surface-100 overflow-hidden border-muted rounded-md border shadow">
<div className="flex flex-col gap-0 divide-y divide-border">
<div className="flex flex-col gap-0 divide-y divide-border-muted">
<div className="grid grid-cols-12 gap-6 px-8 py-8 lg:gap-12">
<div className="relative flex flex-col col-span-12 gap-6 lg:col-span-4">
<p className="text-sm">Upload file size limit</p>
@@ -252,13 +251,7 @@ const StorageSettings = () => {
)}
/>
</div>
<p className="text-sm text-foreground-light">
Optimize and resize images on the fly.{' '}
<InlineLink href="https://supabase.com/docs/guides/storage/serving/image-transformations">
Learn more
</InlineLink>
.
</p>
<Markdown content="Optimize and resize images on the fly. [Learn more](https://supabase.com/docs/guides/storage/serving/image-transformations)." />
</div>
</div>
</div>
+1 -1
View File
@@ -7,7 +7,7 @@ interface InlineLinkProps {
export const InlineLink = ({ href, children }: PropsWithChildren<InlineLinkProps>) => {
const className =
'underline transition underline-offset-2 decoration-foreground-lighter hover:decoration-foreground text-foreground'
'underline transition underline-offset-2 decoration-foreground-lighter hover:decoration-foreground'
if (href.startsWith('http')) {
return (
<a className={className} href={href} target="_blank" rel="noreferrer noopener">
@@ -4,7 +4,6 @@ import { components } from 'data/api'
import { get, handleError } from 'data/fetchers'
import type { ResponseError } from 'types'
import { configKeys } from './keys'
import { IS_PLATFORM } from 'lib/constants'
export type ProjectStorageConfigVariables = {
projectRef?: string
@@ -40,5 +39,5 @@ export const useProjectStorageConfigQuery = <TData = ProjectStorageConfigData>(
useQuery<ProjectStorageConfigData, ProjectStorageConfigError, TData>(
configKeys.storage(projectRef),
({ signal }) => getProjectStorageConfig({ projectRef }, signal),
{ enabled: enabled && IS_PLATFORM && typeof projectRef !== 'undefined', ...options }
{ enabled: enabled && typeof projectRef !== 'undefined', ...options }
)
@@ -1,15 +1,19 @@
import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { handleError, patch } from 'data/fetchers'
import { patch } from 'lib/common/fetch'
import { API_URL } from 'lib/constants'
import type { ResponseError } from 'types'
import { configKeys } from './keys'
import { components } from 'api-types'
type StorageConfigUpdatePayload = components['schemas']['UpdateStorageConfigBody']
export type ProjectStorageConfigUpdateUpdateVariables = StorageConfigUpdatePayload & {
export type ProjectStorageConfigUpdateUpdateVariables = {
projectRef: string
fileSizeLimit: number
features?: {
imageTransformation: {
enabled: boolean
}
}
}
export async function updateProjectStorageConfigUpdate({
@@ -17,12 +21,12 @@ export async function updateProjectStorageConfigUpdate({
fileSizeLimit,
features,
}: ProjectStorageConfigUpdateUpdateVariables) {
const { data, error } = await patch('/platform/projects/{ref}/config/storage', {
params: { path: { ref: projectRef } },
body: { fileSizeLimit, features },
const response = await patch(`${API_URL}/projects/${projectRef}/config/storage`, {
fileSizeLimit,
features,
})
if (error) handleError(error)
return data
if (response.error) throw response.error
return response
}
type ProjectStorageConfigUpdateUpdateData = Awaited<
@@ -3,7 +3,6 @@ import { UseQueryOptions, useQuery } from '@tanstack/react-query'
import { get, handleError } from 'data/fetchers'
import { ResponseError } from 'types'
import { storageCredentialsKeys } from './s3-access-key-keys'
import { IS_PLATFORM } from 'lib/constants'
type StorageCredentialsVariables = { projectRef?: string }
@@ -40,5 +39,5 @@ export const useStorageCredentialsQuery = <TData = StorageCredentialsData>(
useQuery<StorageCredentialsData, ResponseError, TData>(
storageCredentialsKeys.credentials(projectRef),
({ signal }) => fetchStorageCredentials({ projectRef }, signal),
{ enabled: enabled && IS_PLATFORM && typeof projectRef !== 'undefined', ...options }
{ enabled: enabled && typeof projectRef !== 'undefined', ...options }
)
-4
View File
@@ -5681,10 +5681,6 @@ export interface components {
}
StorageFeatures: {
imageTransformation: components['schemas']['StorageFeatureImageTransformation']
s3Protocol: components['schemas']['StorageFeatureS3Protocol']
}
StorageFeatureS3Protocol: {
enabled: boolean
}
StorageObject: {
bucket_id: string