mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 03:45:06 +03:00
chore: image transformation and storage settings entitlements (#42964)
Add entitlement check for storage image transformations to properly control access based on organization's plan. ### Changes - Add `storage.image_transformations` entitlement check to Storage Settings - Add `storage.max_file_size.configurable` entitlement check to Storage Settings - Disable image transformation toggle based on entitlement instead of just free tier check - Wait for entitlement loading before initializing form values - Use entitlement value as fallback when setting default imageTransformationEnabled state - Consolidate all loading states into a single isLoading variable for cleaner code ### Testing - Head to `/project/_/storage/files/settings` with an Org on the Free Plan - Assert that the Image Transformations toggle is disabled - Head to `/project/_/storage/files/settings` with an Org on the Free Plan - Assert that the Image Transformations toggle is enabled - Toggle it, save and refresh the page to assert that your changes were correctly saved
This commit is contained in:
1 parent
d5cc70560d
commit
2942ee9811
1 file changed
+41
-15
@@ -84,7 +84,7 @@ export const StorageSettings = () => {
|
||||
const {
|
||||
data: config,
|
||||
error,
|
||||
isPending: isLoading,
|
||||
isPending: isLoadingProjectStorageConfig,
|
||||
isSuccess,
|
||||
isError,
|
||||
} = useProjectStorageConfigQuery({ projectRef })
|
||||
@@ -104,17 +104,28 @@ export const StorageSettings = () => {
|
||||
const shouldAutoValidateBucketLimits = sizeLimitCheckCondition === 'auto'
|
||||
|
||||
const { data: organization } = useSelectedOrganizationQuery()
|
||||
const { getEntitlementNumericValue, isEntitlementUnlimited } =
|
||||
useCheckEntitlements('storage.max_file_size')
|
||||
const isFreeTier = organization?.plan.id === 'free'
|
||||
const {
|
||||
getEntitlementNumericValue,
|
||||
isEntitlementUnlimited,
|
||||
isLoading: isLoadingMaxFileSizeEntitlement,
|
||||
} = useCheckEntitlements('storage.max_file_size')
|
||||
const { hasAccess: hasAccessToFileSizeConfiguration, isLoading: isLoadingFileSizeConfigurable } =
|
||||
useCheckEntitlements('storage.max_file_size.configurable')
|
||||
const {
|
||||
hasAccess: hasAccessToImageTransformations,
|
||||
isLoading: isLoadingImageTransformationEntitlement,
|
||||
} = useCheckEntitlements('storage.image_transformations')
|
||||
|
||||
const isSpendCapOn =
|
||||
organization?.plan.id === 'pro' && organization?.usage_billing_enabled === false
|
||||
const hasLimitedStorageAccess =
|
||||
!hasAccessToImageTransformations && !hasAccessToFileSizeConfiguration
|
||||
|
||||
const [isUpdating, setIsUpdating] = useState(false)
|
||||
const [initialValues, setInitialValues] = useState<StorageSettingsState>({
|
||||
fileSizeLimit: 0,
|
||||
unit: StorageSizeUnits.MB,
|
||||
imageTransformationEnabled: !isFreeTier,
|
||||
imageTransformationEnabled: false,
|
||||
})
|
||||
|
||||
const maxBytes = useMemo(() => {
|
||||
@@ -125,6 +136,12 @@ export const StorageSettings = () => {
|
||||
}
|
||||
}, [organization, isEntitlementUnlimited, getEntitlementNumericValue])
|
||||
|
||||
const isLoading =
|
||||
isLoadingProjectStorageConfig ||
|
||||
isLoadingPermissions ||
|
||||
isLoadingMaxFileSizeEntitlement ||
|
||||
isLoadingFileSizeConfigurable ||
|
||||
isLoadingImageTransformationEntitlement
|
||||
const FormSchema = z
|
||||
.object({
|
||||
fileSizeLimit: z.coerce.number(),
|
||||
@@ -218,10 +235,11 @@ export const StorageSettings = () => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess && config) {
|
||||
if (isSuccess && config && !isLoading) {
|
||||
const { fileSizeLimit, features } = config
|
||||
const { value, unit } = convertFromBytes(fileSizeLimit ?? 0)
|
||||
const imageTransformationEnabled = features?.imageTransformation?.enabled ?? !isFreeTier
|
||||
const imageTransformationEnabled =
|
||||
features?.imageTransformation?.enabled ?? hasAccessToImageTransformations
|
||||
|
||||
setInitialValues({
|
||||
fileSizeLimit: value,
|
||||
@@ -237,7 +255,7 @@ export const StorageSettings = () => {
|
||||
})
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSuccess, config])
|
||||
}, [isSuccess, config, isLoading, hasAccessToImageTransformations])
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
@@ -250,7 +268,7 @@ export const StorageSettings = () => {
|
||||
title="Storage settings are not available for self-hosted projects"
|
||||
description="Storage settings are only available for Supabase Platform projects."
|
||||
/>
|
||||
) : isLoading || isLoadingPermissions ? (
|
||||
) : isLoading ? (
|
||||
<GenericSkeletonLoader />
|
||||
) : (
|
||||
<>
|
||||
@@ -296,7 +314,9 @@ export const StorageSettings = () => {
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
size="large"
|
||||
disabled={isFreeTier}
|
||||
disabled={
|
||||
!hasAccessToImageTransformations || !canUpdateStorageSettings
|
||||
}
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
@@ -344,7 +364,10 @@ export const StorageSettings = () => {
|
||||
form.clearErrors('fileSizeLimit')
|
||||
}}
|
||||
className="w-32 rounded-r-none border-r-0"
|
||||
disabled={isFreeTier || !canUpdateStorageSettings}
|
||||
disabled={
|
||||
!hasAccessToFileSizeConfiguration ||
|
||||
!canUpdateStorageSettings
|
||||
}
|
||||
/>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
@@ -356,7 +379,10 @@ export const StorageSettings = () => {
|
||||
unitField.onChange(val)
|
||||
form.clearErrors('fileSizeLimit')
|
||||
}}
|
||||
disabled={isFreeTier || !canUpdateStorageSettings}
|
||||
disabled={
|
||||
!hasAccessToFileSizeConfiguration ||
|
||||
!canUpdateStorageSettings
|
||||
}
|
||||
>
|
||||
<SelectTrigger_Shadcn_ className="w-[90px] text-xs font-mono rounded-l-none bg-surface-300">
|
||||
<SelectValue_Shadcn_ placeholder="Choose a prefix">
|
||||
@@ -367,7 +393,7 @@ export const StorageSettings = () => {
|
||||
{Object.values(StorageSizeUnits).map((unit: string) => (
|
||||
<SelectItem_Shadcn_
|
||||
key={unit}
|
||||
disabled={isFreeTier}
|
||||
disabled={!hasAccessToFileSizeConfiguration}
|
||||
value={unit}
|
||||
>
|
||||
{unit}
|
||||
@@ -398,7 +424,7 @@ export const StorageSettings = () => {
|
||||
</FormMessage_Shadcn_>
|
||||
)}
|
||||
</CardContent>
|
||||
{isFreeTier && (
|
||||
{hasLimitedStorageAccess && (
|
||||
<UpgradeToPro
|
||||
fullWidth
|
||||
variant="primary"
|
||||
@@ -447,7 +473,7 @@ export const StorageSettings = () => {
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type={isFreeTier ? 'default' : 'primary'}
|
||||
type={hasLimitedStorageAccess ? 'default' : 'primary'}
|
||||
htmlType="submit"
|
||||
loading={isUpdating}
|
||||
disabled={
|
||||
|
||||
Reference in new issue
Block a user