mirror of
https://github.com/supabase/supabase.git
synced 2026-10-16 22:59:11 +03:00
Revert "Chore/toggle for storage client connection s3 (#31209)"
This reverts commit e00ba41fd1.
This commit is contained in:
1 parent
e00ba41fd1
commit
57318c6060
8 files changed
+73
-218
No files matched your search
+10
-14
@@ -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>
|
||||
|
||||
@@ -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 }
|
||||
)
|
||||
Vendored
-4
@@ -5681,10 +5681,6 @@ export interface components {
|
||||
}
|
||||
StorageFeatures: {
|
||||
imageTransformation: components['schemas']['StorageFeatureImageTransformation']
|
||||
s3Protocol: components['schemas']['StorageFeatureS3Protocol']
|
||||
}
|
||||
StorageFeatureS3Protocol: {
|
||||
enabled: boolean
|
||||
}
|
||||
StorageObject: {
|
||||
bucket_id: string
|
||||
|
||||
Reference in new issue
Block a user