From eaa2f2e68b8c597a0eed66487e89636598435b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Gr=C3=BCneberg?= Date: Thu, 24 Jul 2025 00:43:53 +0800 Subject: [PATCH 1/2] feat: allow 500 GB uploads (#37120) * feat: allow 500 GB uploads * upsell * docs + pricing page * Update StorageSettings.tsx * Small alignment fix --------- Co-authored-by: Joshen Lim --- .../guides/storage/uploads/file-limits.mdx | 6 +-- .../guides/storage/uploads/s3-uploads.mdx | 4 +- .../StorageSettings.constants.ts | 6 ++- .../StorageSettings/StorageSettings.tsx | 47 ++++++++++++++----- apps/studio/components/ui/UpgradeToPro.tsx | 6 ++- packages/shared-data/pricing.ts | 4 +- 6 files changed, 51 insertions(+), 22 deletions(-) diff --git a/apps/docs/content/guides/storage/uploads/file-limits.mdx b/apps/docs/content/guides/storage/uploads/file-limits.mdx index af1628c5bc982..d890c477687bd 100644 --- a/apps/docs/content/guides/storage/uploads/file-limits.mdx +++ b/apps/docs/content/guides/storage/uploads/file-limits.mdx @@ -8,13 +8,13 @@ sidebar_label: 'Limits' ## Global file size -You can set the max file size across all your buckets by setting this global value in the dashboard [here](https://supabase.com/dashboard/project/_/settings/storage). For Free projects, the limit can't exceed 50 MB. On the Pro Plan and up, you can set this value to up to 50 GB. If you need more than 50 GB, [contact us](https://supabase.com/dashboard/support/new). +You can set the max file size across all your buckets by setting this global value in the dashboard [here](https://supabase.com/dashboard/project/_/settings/storage). For Free projects, the limit can't exceed 50 MB. On the Pro Plan and up, you can set this value to up to 500 GB. If you need more than 500 GB, [contact us](https://supabase.com/dashboard/support/new). | Plan | Max File Size Limit | | ---------- | ------------------- | | Free | 50 MB | -| Pro | 50 GB | -| Team | 50 GB | +| Pro | 500 GB | +| Team | 500 GB | | Enterprise | Custom | diff --git a/apps/docs/content/guides/storage/uploads/s3-uploads.mdx b/apps/docs/content/guides/storage/uploads/s3-uploads.mdx index 13f888e2fb596..93c3f4f2be9af 100644 --- a/apps/docs/content/guides/storage/uploads/s3-uploads.mdx +++ b/apps/docs/content/guides/storage/uploads/s3-uploads.mdx @@ -17,7 +17,7 @@ The S3 protocol supports file upload using: The `PutObject` action uploads the file in a single request. This matches the behavior of the Supabase SDK [Standard Upload](/docs/guides/storage/uploads/standard-uploads). -Use `PutObject` to upload smaller files, where retrying the entire upload won't be an issue. The maximum file size on paid plans is 50 GB. +Use `PutObject` to upload smaller files, where retrying the entire upload won't be an issue. The maximum file size on paid plans is 500 GB. For example, using JavaScript and the `aws-sdk` client: @@ -42,7 +42,7 @@ await s3Client.send(uploadCommand) Multipart Uploads split the file into smaller parts and upload them in parallel, maximizing the upload speed on a fast network. When uploading large files, this allows you to retry the upload of individual parts in case of network issues. -This method is preferable over [Resumable Upload](/docs/guides/storage/uploads/resumable-uploads) for server-side uploads, when you want to maximize upload speed at the cost of resumability. The maximum file size on paid plans is 50 GB. +This method is preferable over [Resumable Upload](/docs/guides/storage/uploads/resumable-uploads) for server-side uploads, when you want to maximize upload speed at the cost of resumability. The maximum file size on paid plans is 500 GB. ### Upload a file in parts diff --git a/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.constants.ts b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.constants.ts index b46ecf2cca5d1..411a4fb9507d4 100644 --- a/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.constants.ts +++ b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.constants.ts @@ -1,4 +1,8 @@ -export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES = 50 * 1024 * 1024 * 1024 // 50 GB +export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN = 50 * 1024 * 1024 // 50 MB +/** Max bytes for capped storage (i.e. Pro with spend cap) */ +export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED = 50 * 1024 * 1024 * 1024 // 50 GB +/** Max bytes for uncapped storage (i.e. Pro without spend cap, Team, Enterprise) */ +export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED = 500 * 1024 * 1024 * 1024 // 500 GB export enum StorageSizeUnits { BYTES = 'bytes', diff --git a/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx index 536b07fde314a..43868c50c03ba 100644 --- a/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx +++ b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx @@ -1,7 +1,6 @@ import { zodResolver } from '@hookform/resolvers/zod' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { Clock } from 'lucide-react' -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' import { toast } from 'sonner' import * as z from 'zod' @@ -16,6 +15,7 @@ import { useProjectStorageConfigQuery } from 'data/config/project-storage-config import { useProjectStorageConfigUpdateUpdateMutation } from 'data/config/project-storage-config-update-mutation' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' +import { formatBytes } from 'lib/helpers' import { Button, FormControl_Shadcn_, @@ -32,7 +32,12 @@ import { Select_Shadcn_, Switch, } from 'ui' -import { STORAGE_FILE_SIZE_LIMIT_MAX_BYTES, StorageSizeUnits } from './StorageSettings.constants' +import { + STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED, + STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN, + STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED, + StorageSizeUnits, +} from './StorageSettings.constants' import { convertFromBytes, convertToBytes } from './StorageSettings.utils' interface StorageSettingsState { @@ -56,6 +61,8 @@ const StorageSettings = () => { const organization = useSelectedOrganization() const isFreeTier = organization?.plan.id === 'free' + const isSpendCapOn = + organization?.plan.id === 'pro' && organization?.usage_billing_enabled === false const [initialValues, setInitialValues] = useState({ fileSizeLimit: 0, @@ -84,9 +91,17 @@ const StorageSettings = () => { } }, [isSuccess, config]) - const formattedMaxSizeBytes = `${new Intl.NumberFormat('en-US').format( - STORAGE_FILE_SIZE_LIMIT_MAX_BYTES - )} bytes` + const maxBytes = useMemo(() => { + if (organization?.plan.id === 'free') { + return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN + } else if (organization?.usage_billing_enabled) { + return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED + } else { + return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED + } + }, [organization]) + + const formattedMaxSizeBytes = `${new Intl.NumberFormat('en-US').format(maxBytes)} bytes` const FormSchema = z .object({ @@ -96,7 +111,7 @@ const StorageSettings = () => { }) .superRefine((data, ctx) => { const { unit, fileSizeLimit } = data - const { value: formattedMaxLimit } = convertFromBytes(STORAGE_FILE_SIZE_LIMIT_MAX_BYTES, unit) + const { value: formattedMaxLimit } = convertFromBytes(maxBytes, unit) if (fileSizeLimit > formattedMaxLimit) { ctx.addIssue({ @@ -156,7 +171,7 @@ const StorageSettings = () => {
-
+
{ limit, storageUnit ).toLocaleString()} bytes. `} - Maximum size in bytes of a file that can be uploaded is 50 GB ( - {formattedMaxSizeBytes}). + Maximum upload file size is {formatBytes(maxBytes)}.

@@ -261,9 +275,18 @@ const StorageSettings = () => { {isFreeTier && (
} primaryText="Free Plan has a fixed upload file size limit of 50 MB." - secondaryText="Upgrade to the Pro Plan for a configurable upload file size limit of up to 50 GB." + secondaryText={`Upgrade to Pro Plan for a configurable upload file size limit of ${formatBytes(STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED)} and unlock image transformations.`} + source="storageSizeLimit" + /> +
+ )} + {isSpendCapOn && ( +
+
diff --git a/apps/studio/components/ui/UpgradeToPro.tsx b/apps/studio/components/ui/UpgradeToPro.tsx index b9c3ddf9ba2a6..a98778b7f39ab 100644 --- a/apps/studio/components/ui/UpgradeToPro.tsx +++ b/apps/studio/components/ui/UpgradeToPro.tsx @@ -69,7 +69,7 @@ const UpgradeToPro = ({ }, }} > - Reset database password + {buttonText || (plan === 'free' ? 'Upgrade to Pro' : 'Enable add on')} ) : (