From 3762c69f8360e269355aab38e5d74784746fdc30 Mon Sep 17 00:00:00 2001 From: dannykng Date: Mon, 28 Nov 2022 10:36:02 -0800 Subject: [PATCH 01/45] Break out Storage quickstart --- .../Navigation/Navigation.constants.ts | 1 + apps/docs/pages/guides/storage.mdx | 208 +---------------- apps/docs/pages/guides/storage/quickstart.mdx | 216 ++++++++++++++++++ 3 files changed, 219 insertions(+), 206 deletions(-) create mode 100644 apps/docs/pages/guides/storage/quickstart.mdx diff --git a/apps/docs/components/Navigation/Navigation.constants.ts b/apps/docs/components/Navigation/Navigation.constants.ts index 47c2bee3335..565d27b4ce8 100644 --- a/apps/docs/components/Navigation/Navigation.constants.ts +++ b/apps/docs/components/Navigation/Navigation.constants.ts @@ -244,6 +244,7 @@ export const menuItems: NavMenu = { label: 'Storage', items: [ { name: 'Overview', url: '/guides/storage', items: [] }, + { name: 'Quickstart', url: '/guides/storage/quickstart', items: [] }, { name: 'CDN', url: '/guides/storage-cdn', items: [] }, ], }, diff --git a/apps/docs/pages/guides/storage.mdx b/apps/docs/pages/guides/storage.mdx index bbf3df61c91..f9def048f2b 100644 --- a/apps/docs/pages/guides/storage.mdx +++ b/apps/docs/pages/guides/storage.mdx @@ -34,209 +34,7 @@ keep all video files in a "video" bucket, and profile pictures in an "avatar" bu > -## Getting started - -This is a quick guide that shows the basic functionality of Supabase Storage. Find a full -[example application in GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management), -which you can deploy yourself. - -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fsupabase%2Fsupabase%2Ftree%2Fmaster%2Fexamples%2Fuser-management%2Fnextjs-ts-user-management&project-name=supabase-user-management&repository-name=supabase-user-management&demo-title=Supabase%20User%20Management&demo-description=An%20example%20web%20app%20using%20Supabase%20and%20Next.js&demo-url=https%3A%2F%2Fsupabase-nextjs-ts-user-management.vercel.app&demo-image=https%3A%2F%2Fi.imgur.com%2FZ3HkQqe.png&integration-ids=oac_jUduyjQgOyzev1fjrW83NYOv&external-id=nextjs-user-management) - - - -File, Folder, and Bucket names **must follow** [AWS object key naming guidelines](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html) and avoid use of any other characters. - - - -### Create a bucket - -You can create a bucket using the Supabase Dashboard. -Since the storage is interoperable with your Postgres database, you can also use SQL or our -client libraries. Here we create a bucket called "avatars": - - - - -1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. -2. Click **New Bucket** and enter a name for the bucket. -3. Click **Create Bucket**. - - - - -```sql --- Use Postgres to create a bucket. - -insert into storage.buckets (id, name) -values ('avatars', 'avatars'); -``` - - - - -```js -// Use the JS library to create a bucket. - -const { data, error } = await supabase.storage.createBucket('avatars') -``` - -[Reference.](/docs/reference/javascript/storage-createbucket) - - - - -```dart -void main() async { - final supabase = SupabaseClient('supabaseUrl', 'supabaseKey'); - - final storageResponse = await supabase - .storage - .createBucket('avatars'); -} -``` - -[Reference.](https://pub.dev/documentation/storage_client/latest/storage_client/SupabaseStorageClient/createBucket.html) - - - - -### Upload a file - -You can upload a file from the Dashboard, or within a browser using our JS libraries. - - - - -1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. -2. Select the bucket you want to upload the file to. -3. Click **Upload File**. -4. Select the file you want to upload. - - - - -```js -const avatarFile = event.target.files[0] -const { data, error } = await supabase.storage - .from('avatars') - .upload('public/avatar1.png', avatarFile) -``` - -[Reference.](/docs/reference/javascript/storage-from-upload) - - - - -```dart -void main() async { - final supabase = SupabaseClient('supabaseUrl', 'supabaseKey'); - - // Create file `example.txt` and upload it in `public` bucket - final file = File('example.txt'); - file.writeAsStringSync('File content'); - final storageResponse = await supabase - .storage - .from('public') - .upload('example.txt', file); -} -``` - -[Reference.](https://pub.dev/documentation/storage_client/latest/storage_client/SupabaseStorageClient/createBucket.html) - - - - -### Download a file - -You can download a file from the Dashboard, or within a browser using our JS libraries. - - - - -1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. -2. Select the bucket that contains the file. -3. Select the file that you want to download. -4. Click **Download**. - - - - -```js -// Use the JS library to download a file. - -const { data, error } = await supabase.storage.from('avatars').download('public/avatar1.png') -``` - -[Reference.](/docs/reference/javascript/storage-from-download) - - - - -```dart -void main() async { - final supabase = SupabaseClient('supabaseUrl', 'supabaseKey'); - - final storageResponse = await supabase - .storage - .from('public') - .download('example.txt'); -} -``` - -[Reference.](/docs/reference/dart/storage-from-download) - - - - -### Add security rules - -To restrict access to your files you can use either the Dashboard or SQL. - - - - -1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. -2. Click **Policies** in the sidebar. -3. Click **Add Policies** in the `OBJECTS` table to add policies for Files. You can also create policies for Buckets. -4. Choose whether you want the policy to apply to downloads (SELECT), uploads (INSERT), updates (UPDATE), or deletes (DELETE). -5. Give your policy a unique name. -6. Write the policy using SQL. - - - - -```sql --- Use SQL to create a policy. - -create policy "Public Access" - on storage.objects for select - using ( bucket_id = 'public' ); -``` - - - - -## Public and Private Buckets +### Public and Private Buckets Storage buckets are private by default. @@ -245,9 +43,7 @@ Alternatively, you can create a publicly shareable URL with an expiry date using which calls the `/object/sign/` API. For public buckets, you can access the assets directly without a token or an Authorisation header. The [getPublicUrl](/docs/reference/javascript/storage-from-getpublicurl) -helper method returns the full public URL for an asset. This calls the `/object/public/` API endpoint internally. While no authorization is required for accessing objects in a public bucket, proper [access control](#access-control) is required for other operations like uploading, deleting objects from public buckets as well. - -Public buckets also tend to have [better performance](/docs/guides/storage-cdn#public-vs-private-buckets). +helper method returns the full public URL for an asset. This calls the `/object/public/` API endpoint internally. While no authorization is required for accessing objects in a public bucket, proper [access control](#access-control) is required for other operations like uploading, deleting objects from public buckets as well. Public buckets also tend to have [better performance](/docs/guides/storage-cdn#public-vs-private-buckets).
Advanced: reverse proxy diff --git a/apps/docs/pages/guides/storage/quickstart.mdx b/apps/docs/pages/guides/storage/quickstart.mdx new file mode 100644 index 00000000000..20b99aee99f --- /dev/null +++ b/apps/docs/pages/guides/storage/quickstart.mdx @@ -0,0 +1,216 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'storage-quickstart', + title: 'Storage Quickstart', + description: 'Learn how to use Supabase to store and serve files.', + sidebar_label: 'Quickstart', +} + +This guide shows the basic functionality of Supabase Storage. Find a full [example application on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management) you can deploy yourself. + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fsupabase%2Fsupabase%2Ftree%2Fmaster%2Fexamples%2Fuser-management%2Fnextjs-ts-user-management&project-name=supabase-user-management&repository-name=supabase-user-management&demo-title=Supabase%20User%20Management&demo-description=An%20example%20web%20app%20using%20Supabase%20and%20Next.js&demo-url=https%3A%2F%2Fsupabase-nextjs-ts-user-management.vercel.app&demo-image=https%3A%2F%2Fi.imgur.com%2FZ3HkQqe.png&integration-ids=oac_jUduyjQgOyzev1fjrW83NYOv&external-id=nextjs-user-management) + + + +File, Folder, and Bucket names **must follow** [AWS object key naming guidelines](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html) and avoid use of any other characters. + + + +## Create a bucket + +You can create a bucket using the Supabase Dashboard. +Since the storage is interoperable with your Postgres database, you can also use SQL or our +client libraries. Here we create a bucket called "avatars": + + + + +1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. +2. Click **New Bucket** and enter a name for the bucket. +3. Click **Create Bucket**. + + + + +```sql +-- Use Postgres to create a bucket. + +insert into storage.buckets (id, name) +values ('avatars', 'avatars'); +``` + + + + +```js +// Use the JS library to create a bucket. + +const { data, error } = await supabase.storage.createBucket('avatars') +``` + +[Reference.](/docs/reference/javascript/storage-createbucket) + + + + +```dart +void main() async { + final supabase = SupabaseClient('supabaseUrl', 'supabaseKey'); + + final storageResponse = await supabase + .storage + .createBucket('avatars'); +} +``` + +[Reference.](https://pub.dev/documentation/storage_client/latest/storage_client/SupabaseStorageClient/createBucket.html) + + + + +## Upload a file + +You can upload a file from the Dashboard, or within a browser using our JS libraries. + + + + +1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. +2. Select the bucket you want to upload the file to. +3. Click **Upload File**. +4. Select the file you want to upload. + + + + +```js +const avatarFile = event.target.files[0] +const { data, error } = await supabase.storage + .from('avatars') + .upload('public/avatar1.png', avatarFile) +``` + +[Reference.](/docs/reference/javascript/storage-from-upload) + + + + +```dart +void main() async { + final supabase = SupabaseClient('supabaseUrl', 'supabaseKey'); + + // Create file `example.txt` and upload it in `public` bucket + final file = File('example.txt'); + file.writeAsStringSync('File content'); + final storageResponse = await supabase + .storage + .from('public') + .upload('example.txt', file); +} +``` + +[Reference.](https://pub.dev/documentation/storage_client/latest/storage_client/SupabaseStorageClient/createBucket.html) + + + + +## Download a file + +You can download a file from the Dashboard, or within a browser using our JS libraries. + + + + +1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. +2. Select the bucket that contains the file. +3. Select the file that you want to download. +4. Click **Download**. + + + + +```js +// Use the JS library to download a file. + +const { data, error } = await supabase.storage.from('avatars').download('public/avatar1.png') +``` + +[Reference.](/docs/reference/javascript/storage-from-download) + + + + +```dart +void main() async { + final supabase = SupabaseClient('supabaseUrl', 'supabaseKey'); + + final storageResponse = await supabase + .storage + .from('public') + .download('example.txt'); +} +``` + +[Reference.](/docs/reference/dart/storage-from-download) + + + + +## Add security rules + +To restrict access to your files you can use either the Dashboard or SQL. + + + + +1. Go to the [Storage](https://app.supabase.com/project/_/storage/buckets) page in the Dashboard. +2. Click **Policies** in the sidebar. +3. Click **Add Policies** in the `OBJECTS` table to add policies for Files. You can also create policies for Buckets. +4. Choose whether you want the policy to apply to downloads (SELECT), uploads (INSERT), updates (UPDATE), or deletes (DELETE). +5. Give your policy a unique name. +6. Write the policy using SQL. + + + + +```sql +-- Use SQL to create a policy. + +create policy "Public Access" + on storage.objects for select + using ( bucket_id = 'public' ); +``` + + + + +## See also + +- [Supabase Storage on GitHub](https://github.com/supabase/storage-api) +- [Swagger API documentation](https://supabase.github.io/storage-api) +- Official [JavaScript](/docs/reference/javascript/storage-createbucket) and [Dart](/docs/reference/dart/storage-createbucket) documentation + +export const Page = ({ children }) => + +export default Page From 33aa78fa88d9811c3067be48c58661dd8d702b77 Mon Sep 17 00:00:00 2001 From: dannykng Date: Mon, 28 Nov 2022 10:49:11 -0800 Subject: [PATCH 02/45] Break out Access Control docs --- .../Navigation/Navigation.constants.ts | 1 + apps/docs/pages/guides/storage.mdx | 132 ----------------- .../pages/guides/storage/access-control.mdx | 137 ++++++++++++++++++ 3 files changed, 138 insertions(+), 132 deletions(-) create mode 100644 apps/docs/pages/guides/storage/access-control.mdx diff --git a/apps/docs/components/Navigation/Navigation.constants.ts b/apps/docs/components/Navigation/Navigation.constants.ts index 565d27b4ce8..8000e07e607 100644 --- a/apps/docs/components/Navigation/Navigation.constants.ts +++ b/apps/docs/components/Navigation/Navigation.constants.ts @@ -245,6 +245,7 @@ export const menuItems: NavMenu = { items: [ { name: 'Overview', url: '/guides/storage', items: [] }, { name: 'Quickstart', url: '/guides/storage/quickstart', items: [] }, + { name: 'Access Control', url: '/guides/storage/access-control', items: [] }, { name: 'CDN', url: '/guides/storage-cdn', items: [] }, ], }, diff --git a/apps/docs/pages/guides/storage.mdx b/apps/docs/pages/guides/storage.mdx index f9def048f2b..fd024d292c8 100644 --- a/apps/docs/pages/guides/storage.mdx +++ b/apps/docs/pages/guides/storage.mdx @@ -34,138 +34,6 @@ keep all video files in a "video" bucket, and profile pictures in an "avatar" bu > -### Public and Private Buckets - -Storage buckets are private by default. - -For private buckets, you can access objects via the [download](/docs/reference/javascript/storage-from-download) method. This corresponds to `/object/auth/` API endpoint. -Alternatively, you can create a publicly shareable URL with an expiry date using the [createSignedUrl](/docs/reference/javascript/storage-from-createsignedurl) method -which calls the `/object/sign/` API. - -For public buckets, you can access the assets directly without a token or an Authorisation header. The [getPublicUrl](/docs/reference/javascript/storage-from-getpublicurl) -helper method returns the full public URL for an asset. This calls the `/object/public/` API endpoint internally. While no authorization is required for accessing objects in a public bucket, proper [access control](#access-control) is required for other operations like uploading, deleting objects from public buckets as well. Public buckets also tend to have [better performance](/docs/guides/storage-cdn#public-vs-private-buckets). - -
-Advanced: reverse proxy -The URLs returned are proxied through the API Proxy. They are prefixed by /storage/v1. - -For example, on the hosted Platform they will be - -https://[project_ref].supabase.co/storage/v1/object/public/[id] - -You can access the storage API directly with the same endpoint. See the API docs for a full list of operations available. - -
- ---- - -## Access control - -Supabase Storage is integrated with your [Postgres Database](/docs/guides/database). -This means that you can use the same [Row Level Security Policies](/docs/guides/auth#policies) -for managing access to your files. Supabase Storage stores metadata in the `objects` and `buckets` table in the storage schema. To allow read access to files, the RLS policy must allow users to `SELECT` the `objects` table and for uploading a new object, the RLS policy must grant users access to `INSERT` into the `objects` table and so on. The mapping between the different API calls and the database permissions required is documented in the [Reference docs](/docs/reference/javascript/storage-createbucket). - - - -Access control for Storage is mapped to CRUD operations on the `buckets` and `objects` table via RLS policies. - - - -
- -
- -## Helpers - -Supabase Storage provides SQL helper functions which you can use in your database queries and -policies. - ---- - -#### `storage.filename()` - -Returns the name of a file. - -```sql -select storage.filename(name) -from storage.objects; -``` - -For example, if your file is stored in `public/subfolder/avatar.png` it would return: - -`'avatar.png'` - ---- - -#### `storage.foldername()` - -Returns an array path, with all of the subfolders that a file belongs to. - -```sql -select storage.foldername(name) -from storage.objects; -``` - -For example, if your file is stored in `public/subfolder/avatar.png` it would return: - -`[ 'public', 'subfolder' ]` - ---- - -#### `storage.extension()` - -Returns the extension of a file. - -```sql -select storage.extension(name) -from storage.objects; -``` - -For example, if your file is stored in `public/subfolder/avatar.png` it would return: - -`'png'` - ---- - -## Policy examples - -Here are some examples of storage policies. - -### Allow public access to a bucket - -```sql --- 1. Allow public access to any files in the "public" bucket -create policy "Public Access" -on storage.objects for select -using ( bucket_id = 'public' ); -``` - -### Allow logged-in access to a bucket - -```sql --- 1. Allow logged-in access to any files in the "restricted" bucket -create policy "Restricted Access" -on storage.objects for select -using ( - bucket_id = 'restricted' - and auth.role() = 'authenticated' -); -``` - -### Allow individual access to a file - -```sql --- 1. Allow a user to access their own files -create policy "Individual user Access" -on storage.objects for select -using ( auth.uid() = owner ); -``` - ## See also - Read more about Supabase Storage in the [blog post](https://supabase.com/blog/supabase-storage) diff --git a/apps/docs/pages/guides/storage/access-control.mdx b/apps/docs/pages/guides/storage/access-control.mdx new file mode 100644 index 00000000000..58572bd789e --- /dev/null +++ b/apps/docs/pages/guides/storage/access-control.mdx @@ -0,0 +1,137 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'storage-access-control', + title: 'Access Control', + description: 'Manage who can access your Supabase Storage files.', +} + +Supabase Storage is integrated with your [Postgres Database](/docs/guides/database). +This means that you can use the same [Row Level Security Policies](/docs/guides/auth#policies) +for managing access to your files. Supabase Storage stores metadata in the `objects` and `buckets` table in the storage schema. + +To allow read access to files, the RLS policy must allow users to `SELECT` the `objects` table and for uploading a new object, the RLS policy must grant users access to `INSERT` into the `objects` table and so on. The mapping between the different API calls and the database permissions required is documented in the [Reference docs](/docs/reference/javascript/storage-createbucket). + + + +Access control for Storage is mapped to CRUD operations on the `buckets` and `objects` table via RLS policies. + + + +
+ +
+ +## Public and private buckets + +Storage buckets are private by default. For private buckets, you can access objects via the [download](/docs/reference/javascript/storage-from-download) method. This corresponds to `/object/auth/` API endpoint. +Alternatively, you can create a publicly shareable URL with an expiry date using the [createSignedUrl](/docs/reference/javascript/storage-from-createsignedurl) method +which calls the `/object/sign/` API. + +For public buckets, you can access the assets directly without a token or an Authorisation header. The [getPublicUrl](/docs/reference/javascript/storage-from-getpublicurl) +helper method returns the full public URL for an asset. This calls the `/object/public/` API endpoint internally. While no authorization is required for accessing objects in a public bucket, proper [access control](#access-control) is required for other operations like uploading, deleting objects from public buckets as well. Public buckets also tend to have [better performance](/docs/guides/storage-cdn#public-vs-private-buckets). + +
+Advanced: reverse proxy +The URLs returned are proxied through the API Proxy. They are prefixed by /storage/v1. + +For example, on the hosted Platform they will be + +https://[project_ref].supabase.co/storage/v1/object/public/[id] + +You can access the storage API directly with the same endpoint. See the API docs for a full list of operations available. + +
+ +## Helper functions + +Supabase Storage provides SQL helper functions which you can use in your database queries and +policies. + +--- + +#### `storage.filename()` + +Returns the name of a file. + +```sql +select storage.filename(name) +from storage.objects; +``` + +For example, if your file is stored in `public/subfolder/avatar.png` it would return: + +`'avatar.png'` + +--- + +#### `storage.foldername()` + +Returns an array path, with all of the subfolders that a file belongs to. + +```sql +select storage.foldername(name) +from storage.objects; +``` + +For example, if your file is stored in `public/subfolder/avatar.png` it would return: + +`[ 'public', 'subfolder' ]` + +--- + +#### `storage.extension()` + +Returns the extension of a file. + +```sql +select storage.extension(name) +from storage.objects; +``` + +For example, if your file is stored in `public/subfolder/avatar.png` it would return: + +`'png'` + +--- + +## Policy examples + +### Allow public access to a bucket + +```sql +-- 1. Allow public access to any files in the "public" bucket +create policy "Public Access" +on storage.objects for select +using ( bucket_id = 'public' ); +``` + +### Allow logged-in access to a bucket + +```sql +-- 1. Allow logged-in access to any files in the "restricted" bucket +create policy "Restricted Access" +on storage.objects for select +using ( + bucket_id = 'restricted' + and auth.role() = 'authenticated' +); +``` + +### Allow individual access to a file + +```sql +-- 1. Allow a user to access their own files +create policy "Individual user Access" +on storage.objects for select +using ( auth.uid() = owner ); +``` + +export const Page = ({ children }) => + +export default Page From 3f0be7e1520a15b2e5abf6ff5533689bd5cf7a5b Mon Sep 17 00:00:00 2001 From: dannykng Date: Mon, 28 Nov 2022 10:59:07 -0800 Subject: [PATCH 03/45] Move Storage CDN doc to the Storage directory --- .../Navigation/Navigation.constants.ts | 2 +- .../guides/{storage-cdn.mdx => storage/cdn.mdx} | 16 ++++------------ apps/www/lib/redirects.js | 5 +++++ 3 files changed, 10 insertions(+), 13 deletions(-) rename apps/docs/pages/guides/{storage-cdn.mdx => storage/cdn.mdx} (73%) diff --git a/apps/docs/components/Navigation/Navigation.constants.ts b/apps/docs/components/Navigation/Navigation.constants.ts index 8000e07e607..5fe01d07856 100644 --- a/apps/docs/components/Navigation/Navigation.constants.ts +++ b/apps/docs/components/Navigation/Navigation.constants.ts @@ -246,7 +246,7 @@ export const menuItems: NavMenu = { { name: 'Overview', url: '/guides/storage', items: [] }, { name: 'Quickstart', url: '/guides/storage/quickstart', items: [] }, { name: 'Access Control', url: '/guides/storage/access-control', items: [] }, - { name: 'CDN', url: '/guides/storage-cdn', items: [] }, + { name: 'CDN', url: '/guides/storage/cdn', items: [] }, ], }, { diff --git a/apps/docs/pages/guides/storage-cdn.mdx b/apps/docs/pages/guides/storage/cdn.mdx similarity index 73% rename from apps/docs/pages/guides/storage-cdn.mdx rename to apps/docs/pages/guides/storage/cdn.mdx index 4c11ca8fe64..1d5023c7dc0 100644 --- a/apps/docs/pages/guides/storage-cdn.mdx +++ b/apps/docs/pages/guides/storage/cdn.mdx @@ -3,27 +3,19 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'storage-cdn', title: 'Storage CDN', - description: 'How Supabase Storage caches objects with a CDN', + description: 'Learn how Supabase Storage caches objects with a CDN.', sidebar_label: 'CDN', } -## CDN basics +All assets uploaded to Supabase Storage are cached on a Content Delivery Network (CDN) to improve the latency for users all around the world. CDNs are a geographically distributed set of servers or **nodes** which caches content from an **origin server**. For Supabase Storage, the origin is the storage server running in the [same region as your project](https://app.supabase.com/project/_/settings/general). Aside from performance, CDNs also help with security and availability by mitigating Distributed Denial of Service and other application attacks. -All assets uploaded to storage are cached on a Content Delivery Network (CDN). - -To improve the latency for users all around the world, Supabase uses a CDN. CDNs are a geographically distributed set of servers or **nodes** which caches content from an **origin server**. For Supabase Storage, the origin is the storage server running in the [same region as your project](https://app.supabase.com/project/_/settings/general). +## Example Let’s walk through an example of how a CDN helps with performance. A new bucket is created for a Supabase project launched in Singapore. All requests to the Supabase Storage API first hit the CDN. A user from the United States requests an object and is routed to the U.S. CDN. At this point, that CDN node does not have the object in its cache and pings the origin server in Singapore. Another user, also in the United States, requests the same object and is served directly from the CDN cache in the United States instead of routing the request back to Singapore. -Aside from performance, CDNs also help with security and availability by mitigating Distributed Denial of Service and other application attacks. - ## Cache duration -By default, assets are cached both in the CDN and in the user's browser for 1 hour. After this, the CDN nodes ping the storage server to see if an object has been updated. - -You can modify this cache time when you are [uploading](/docs/reference/javascript/storage-from-upload) or [updating](/docs/reference/javascript/storage-from-update) an object by modifying the `cacheControl` parameter. - -If you expect the object to not change at a given URL, setting a longer cache duration is preferable. +By default, assets are cached both in the CDN and in the user's browser for 1 hour. After this, the CDN nodes ping the storage server to see if an object has been updated. You can modify this cache time when you are [uploading](/docs/reference/javascript/storage-from-upload) or [updating](/docs/reference/javascript/storage-from-update) an object by modifying the `cacheControl` parameter. If you expect the object to not change at a given URL, setting a longer cache duration is preferable. If you need to update the version of the object stored in the CDN, there are various cache-busting techniques you can use. The most common way to do this is to add a version query parameter in the URL. For example, you can use a URL like `/storage/v1/object/sign/profile-pictures/cat.jpg?token=eyJh...&version=1` in your applications and set a long cache time of 1 year. When you want to update the cat picture, you can increment the version query parameter in the URL. The CDN will treat `/storage/v1/object/sign/profile-pictures/cat.jpg?token=eyJh...&version=2` as a new object and pings the origin for the updated version. diff --git a/apps/www/lib/redirects.js b/apps/www/lib/redirects.js index c370fc9ce29..9edc1371bfa 100644 --- a/apps/www/lib/redirects.js +++ b/apps/www/lib/redirects.js @@ -1560,4 +1560,9 @@ module.exports = [ source: '/forgot-password', destination: 'https://app.supabase.com/forgot-password', }, + { + permanent: true, + source: '/docs/guides/storage-cdn', + destination: '/docs/guides/storage/cdn', + }, ] From 830864fd081b24d7dd190e483e74c40322a9b782 Mon Sep 17 00:00:00 2001 From: dannykng Date: Mon, 28 Nov 2022 16:49:33 -0800 Subject: [PATCH 04/45] Update redirect --- apps/www/lib/redirects.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/lib/redirects.js b/apps/www/lib/redirects.js index 9edc1371bfa..30a75eb6016 100644 --- a/apps/www/lib/redirects.js +++ b/apps/www/lib/redirects.js @@ -7,7 +7,7 @@ module.exports = [ { permanent: true, source: '/docs/guides/storage-caching', - destination: '/docs/guides/storage-cdn', + destination: '/docs/guides/storage/cdn', }, { permanent: true, From 4ff57fa8c4e56e0633b75a1164f0d7ec8a550f6b Mon Sep 17 00:00:00 2001 From: David Wolf <68775926+devidw@users.noreply.github.com> Date: Sun, 4 Dec 2022 18:16:41 +0100 Subject: [PATCH 05/45] Add missing import `AuthApiError` --- apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx b/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx index 62e6a0154e0..e6d1d141100 100644 --- a/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx +++ b/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx @@ -295,6 +295,7 @@ If you try to submit a form with the action `?/createPost` without a valid sessi import type { Actions } from './$types' import { invalid, redirect } from '@sveltejs/kit' import { getSupabase } from '@supabase/auth-helpers-sveltekit' +import { AuthApiError } from '@supabase/supabase-js' export const actions: Actions = { signin: async (event) => { From b0c516bd1b9395dfacf436324b54750c7bc1c94c Mon Sep 17 00:00:00 2001 From: Lakshan Perera Date: Mon, 5 Dec 2022 15:15:09 +1100 Subject: [PATCH 06/45] fix: added file upload to storage example --- .../functions/file-upload-storage/index.ts | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 examples/edge-functions/supabase/functions/file-upload-storage/index.ts diff --git a/examples/edge-functions/supabase/functions/file-upload-storage/index.ts b/examples/edge-functions/supabase/functions/file-upload-storage/index.ts new file mode 100644 index 00000000000..5593c0b6581 --- /dev/null +++ b/examples/edge-functions/supabase/functions/file-upload-storage/index.ts @@ -0,0 +1,66 @@ +// This example shows how to use Edge Functions to read incoming multipart/form-data request, +// and write files to Supabase Storage and other fields to a database table. + +import { Application, Router } from 'https://deno.land/x/oak/mod.ts' +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' + +const MB = 1024 * 1024 + +const app = new Application() + +app.use(async (ctx) => { + const body = ctx.request.body({ type: 'form-data' }) + const formData = await body.value.read({ + // Need to set the maxSize so files will be stored in memory. + // This is necessary as Edge Functions don't have disk write access. + // We are setting the max size as 10MB (an Edge Function has a max memory limit of 150MB) + // For more config options, check: https://deno.land/x/oak@v11.1.0/mod.ts?s=FormDataReadOptions + maxSize: 10 * MB, + }) + if (!formData.files || !formData.files.length) { + ctx.response.status = 400 + ctx.response.body = 'missing file' + return + } + + const supabaseClient = createClient( + // Supabase API URL - env var exported by default. + Deno.env.get('SUPABASE_URL') ?? '', + // Supabase API ANON KEY - env var exported by default. + Deno.env.get('SUPABASE_ANON_KEY') ?? '' + ) + + //upload image to Storage + const file = formData.files[0] + const timestamp = +new Date() + const uploadName = `${file.name}-${timestamp}` + const { data: upload, error: uploadError } = await supabaseClient.storage + .from('images') + .upload(uploadName, file.content!.buffer, { + contentType: file.contentType, + cacheControl: '3600', + upsert: false, + }) + if (uploadError) { + console.error(uploadError) + ctx.response.status = 500 + ctx.response.body = 'Failed to upload the file' + return + } + + // insert record to messages table + const { error } = await supabaseClient + .from('comments') + .insert({ message: formData.fields!.message || '', image_path: upload.path }) + if (error) { + console.error(error) + ctx.response.status = 500 + ctx.response.body = 'Fail to add the record' + return + } + + ctx.response.status = 201 + ctx.response.body = 'Success!' +}) + +await app.listen({ port: 8000 }) From be40f5e5c8a8eb51574ab652be970d9a09192c81 Mon Sep 17 00:00:00 2001 From: Lakshan Perera Date: Mon, 5 Dec 2022 15:18:25 +1100 Subject: [PATCH 07/45] fix: add README --- .../supabase/functions/file-upload-storage/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 examples/edge-functions/supabase/functions/file-upload-storage/README.md diff --git a/examples/edge-functions/supabase/functions/file-upload-storage/README.md b/examples/edge-functions/supabase/functions/file-upload-storage/README.md new file mode 100644 index 00000000000..3d061d5d78e --- /dev/null +++ b/examples/edge-functions/supabase/functions/file-upload-storage/README.md @@ -0,0 +1,3 @@ +# Upload files to Storage + +This example shows how to process `multipart/form-data` in an Edge Function. Then, upload files in the request to Supabase Storage and insert other fields as a record to a database table. From 8bcdfe11ac48f77fa5bd220a00fb60ac1d7d81ac Mon Sep 17 00:00:00 2001 From: anderson Date: Mon, 5 Dec 2022 02:35:34 -0300 Subject: [PATCH 08/45] updated the portuguese README translations --- i18n/README.pt-br.md | 58 +++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/i18n/README.pt-br.md b/i18n/README.pt-br.md index fa90c744008..9d22a6c7282 100644 --- a/i18n/README.pt-br.md +++ b/i18n/README.pt-br.md @@ -27,11 +27,14 @@ Para documentação completa, visite [supabase.com/docs](https://supabase.com/docs) +Para ver como Contribuir, visite [Primeiros Passos](./DEVELOPERS.md) + ## Comunidade & Suporte - [Fórum da comunidade](https://github.com/supabase/supabase/discussions). Melhor para: ajuda com a construção, discussões sobre melhores práticas em banco de dados. - [GitHub Issues](https://github.com/supabase/supabase/issues). Melhor para: problemas ou erros que você encontrar usando o Supabase. - [Suporte por E-mail](https://supabase.com/docs/support#business-support). Melhor para: problemas com o seu banco de dados ou infraestrutura. +- [Discord](https://discord.supabase.com). Melhor para: compartilhar seus aplicativos e se divertir com a comunidade. ## Status @@ -53,6 +56,7 @@ Supabase é uma combinação de ferramentas de código aberto. Nós estamos cons **Arquitetura atual** Supabase é uma [plataforma hospedada](https://app.supabase.com). Você pode se cadastrar e começar a usar Supabase sem instalar nada. Nós ainda estamos criando a experiência de trabalho local - esse é o nosso foco principal, junto com a estabilidade da plataforma. +Voce tambem pode [auto-hospedar](https://supabase.com/docs/guides/hosting/overview) e [develop locally](https://supabase.com/docs/guides/local-development). ![Arquitetura](https://user-images.githubusercontent.com/70828596/187547862-ffa9d058-0c3a-4851-a3e7-92ccfca4b596.png) @@ -66,7 +70,7 @@ Supabase é uma [plataforma hospedada](https://app.supabase.com). Você pode se #### Bibliotecas de cliente -Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementação independente para cada sistema externo. Esse é uma das formas de apoiar as ferramentas existentes. +Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementação independente para cada sistema externo. Essa é uma das formas de apoiar as ferramentas existentes. @@ -81,6 +85,7 @@ Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementaç + - + @@ -101,8 +106,18 @@ Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementaç + - + + + + + + + + + + @@ -110,22 +125,16 @@ Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementaç - - - - - - - - + + - - + + @@ -134,14 +143,16 @@ Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementaç + - + - - + + + @@ -149,7 +160,8 @@ Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementaç - + + @@ -158,6 +170,7 @@ Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementaç + @@ -166,6 +179,7 @@ Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementaç + @@ -174,6 +188,16 @@ Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementaç + + + + + + + + + +
GoTrue Realtime StorageFunções
⚡️ Oficial ⚡️⚡️ Oficial ⚡️
JavaScript (TypeScript) supabase-jsgotrue-js realtime-js storage-jsfunctions-js
💚 Comunidade 💚
Fluttersupabase-flutterpostgrest-dartgotrue-dartrealtime-dartstorage-dartfunctions-dart
💚 Comunidade 💚
C# supabase-csharpgotrue-csharp realtime-csharp storage-csharp
Fluttersupabase-dartpostgrest-dartgotrue-langrealtime-dartstorage-dartfunctions-csharp
Go - postgrest-gogotrue-go ---storage-gofunctions-go
Javagotrue-java - --
Kotlin-supabase-kt postgrest-kt gotrue-kt--realtime-ktstorage-ktfunctions-kt
Pythonpostgrest-py gotrue-py realtime-py-storage-pyfunctions-py
Ruby- - --
Rust- - --
Swiftgotrue-swift realtime-swift storage-swiftfunctions-swift
Godot Engine (GDScript)supabase-gdscriptpostgrest-gdscriptgotrue-gdscriptrealtime-gdscriptstorage-gdscriptfunctions-gdscript
From 0e0fa5799b638a08e0b19902d19bf6bc1a04c8a0 Mon Sep 17 00:00:00 2001 From: Ollie Halliday <22655069+Hallidayo@users.noreply.github.com> Date: Mon, 5 Dec 2022 16:33:12 +0000 Subject: [PATCH 09/45] changed docs url --- DEVELOPERS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVELOPERS.md b/DEVELOPERS.md index 585e7359e3d..d4a62cd88f9 100644 --- a/DEVELOPERS.md +++ b/DEVELOPERS.md @@ -75,7 +75,7 @@ Then visit, and edit, any of the following sites: | ---------------------------------------------- | -------------- | ---------- | ------------------------------------ | ------------------------ | | [supabase.com](https://supabase.com) | `/apps/www` | www | The main website | http://localhost:3000 | | [app.supabase.com](https://app.supabase.com) | `/studio` | studio | Studio dashboard | http://localhost:8082 | -| [supabase.com/docs](https://supabase.com/docs) | `/apps/docs` | docs | Guides and Reference (Next.js based) | http://localhost:3001 | +| [supabase.com/docs](https://supabase.com/docs) | `/apps/docs` | docs | Guides and Reference (Next.js based) | http://localhost:3001/docs | #### Running sites individually From 78497876939900c46c6ead3a587bdcc2018ac71b Mon Sep 17 00:00:00 2001 From: dng Date: Mon, 5 Dec 2022 13:42:22 -0800 Subject: [PATCH 10/45] Update pgnet.mdx --- apps/docs/pages/guides/database/extensions/pgnet.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/pages/guides/database/extensions/pgnet.mdx b/apps/docs/pages/guides/database/extensions/pgnet.mdx index d652be96ad8..f9b99c7e018 100644 --- a/apps/docs/pages/guides/database/extensions/pgnet.mdx +++ b/apps/docs/pages/guides/database/extensions/pgnet.mdx @@ -8,7 +8,7 @@ export const meta = { -The pg_net API is in beta. Functions signatures may change. +The pg_net API is in alpha. Functions signatures may change. From c6cb6e37223f1ba0d5220d40f02cc39c0b2de423 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 2 Dec 2022 16:55:35 +0800 Subject: [PATCH 11/45] Small improvements to custom domains ui --- .../interfaces/Billing/AddOns/AddOns.utils.ts | 11 +++++--- .../CustomDomainConfig/CustomDomainConfig.tsx | 2 +- .../CustomDomainConfig/CustomDomainVerify.tsx | 26 ++++++++++++++++--- .../CustomDomainsConfigureHostname.tsx | 2 +- studio/components/ui/UpgradeToPro.tsx | 2 +- 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts b/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts index 4440c0a0f78..9b1a7bf7d68 100644 --- a/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts +++ b/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts @@ -1,12 +1,17 @@ import { DatabaseAddon } from './AddOns.types' -// e.g supabase-postgres-14.1.0.88 (There's 4 segments instead, so we can't use semver) export const getSemanticVersion = (version: string) => { if (!version) return 0 - const segments = version.split('-') + // e.g supabase-postgres-14.1.0.88 + // There's 4 segments instead so we can't use the semver package + const segments = version.split('supabase-postgres-') const semver = segments[segments.length - 1] - return Number(semver.split('.').join('')) + + // e.g supabase-postgres-14.1.0.99-vault-rc1 + const formattedSemver = semver.split('-')[0] + + return Number(formattedSemver.split('.').join('')) } export const formatComputeSizes = (addons: DatabaseAddon[]) => { diff --git a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx index dde95501290..be80c2ac9d8 100644 --- a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx +++ b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx @@ -35,7 +35,7 @@ const CustomDomainConfig = () => { return (
- + {data?.status === '0_no_hostname_configured' ? ( ) : ( diff --git a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx index c8dee15035a..ab3a0664e3d 100644 --- a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx +++ b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainVerify.tsx @@ -83,9 +83,29 @@ const CustomDomainVerify = ({ projectRef, customDomain, settings }: CustomDomain : 'Please note that it may take up to 24 hours for the DNS records to propagate.' } description={ - isNotVerifiedYet - ? 'Do check again in a bit as it may take up to 24 hours for changes in DNS records to propagate.' - : undefined + isNotVerifiedYet ? ( +
+

+ Do check again in a bit as it may take up to 24 hours for changes in DNS + records to propagate. +

+

+ You may also visit{' '} + + here + {' '} + to check if your DNS has been propagated successfully before clicking verify. +

+
+ ) : ( +

+ You may also visit{' '} + + here + {' '} + to check if your DNS has been propagated successfully before clicking verify. +

+ ) } /> diff --git a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx index a79ced1679c..5fa62056d20 100644 --- a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx +++ b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx @@ -88,7 +88,7 @@ const CustomDomainsConfigureHostname = ({ projectRef }: CustomDomainsConfigureHo } > - Add a Custom Domain}> + Add a custom domain}> = ({ icon, primaryText, projectRef, secondaryText >
{icon &&
{icon}
} -
+

{primaryText}

From af925000ab61fae8d78b53f0f21aad0aab1397d5 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Sat, 3 Dec 2022 01:10:05 +0800 Subject: [PATCH 12/45] Fix toast message --- .../components/interfaces/App/PortalToast.tsx | 18 ++++++++++-------- studio/styles/toast.scss | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/studio/components/interfaces/App/PortalToast.tsx b/studio/components/interfaces/App/PortalToast.tsx index 14cc701e890..18434e82a2e 100644 --- a/studio/components/interfaces/App/PortalToast.tsx +++ b/studio/components/interfaces/App/PortalToast.tsx @@ -32,14 +32,16 @@ const PortalToast = () => ( {({ icon, message }) => ( <> {icon} - {message} - {t.type !== 'loading' && ( -
- -
- )} +
+
{message}
+ {t.type !== 'loading' && ( +
+ +
+ )} +
)} diff --git a/studio/styles/toast.scss b/studio/styles/toast.scss index 87b0ef9214c..88dd2d08fcf 100644 --- a/studio/styles/toast.scss +++ b/studio/styles/toast.scss @@ -552,4 +552,4 @@ animation-name: Toastify__slideOutDown; } -/*# sourceMappingURL=ReactToastify.css.map */ +/*# sourceMappingURL=ReactToastify.css.map */ \ No newline at end of file From 6064b9d8a890cd0c84bd355e75d79bb83ff94934 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 5 Dec 2022 14:44:16 +0800 Subject: [PATCH 13/45] Remove enablePermissions flag --- .../components/interfaces/App/PortalToast.tsx | 2 +- .../PaymentSummaryPanel.tsx | 14 +-- .../Billing/Subscription/Subscription.tsx | 12 +- .../BillingAddress/BillingAddress.tsx | 10 +- .../BillingSettings/BillingEmail.tsx | 17 +-- .../BillingSettings/BillingSettings.tsx | 11 +- .../BillingSettings/PaymentMethods.tsx | 13 +- .../BillingSettings/TaxID/TaxID.tsx | 6 +- .../GeneralSettings/GeneralSettings.tsx | 14 +-- .../NotOrganizationOwnerWarning.tsx | 8 +- .../TeamSettings/InviteMemberButton.tsx | 16 +-- .../TeamSettings/MemberActions.tsx | 23 ++-- .../Organization/TeamSettings/MembersView.tsx | 117 ++++++++---------- .../TeamSettings/TeamSettings.tsx | 6 +- .../DeleteProjectButton.tsx | 9 +- .../interfaces/Settings/General/General.tsx | 11 +- .../Infrastructure/PauseProjectButton.tsx | 12 +- .../ProjectLayout/ProjectPausedState.tsx | 14 +-- studio/pages/new/[slug].tsx | 8 +- .../pages/project/[ref]/settings/database.tsx | 5 +- studio/styles/toast.scss | 8 ++ 21 files changed, 125 insertions(+), 211 deletions(-) diff --git a/studio/components/interfaces/App/PortalToast.tsx b/studio/components/interfaces/App/PortalToast.tsx index 18434e82a2e..67386cf12b7 100644 --- a/studio/components/interfaces/App/PortalToast.tsx +++ b/studio/components/interfaces/App/PortalToast.tsx @@ -33,7 +33,7 @@ const PortalToast = () => ( <> {icon}
-
{message}
+
{message}
{t.type !== 'loading' && (
diff --git a/studio/components/interfaces/Billing/Subscription/Subscription.tsx b/studio/components/interfaces/Billing/Subscription/Subscription.tsx index 22909243baa..aa49e690d66 100644 --- a/studio/components/interfaces/Billing/Subscription/Subscription.tsx +++ b/studio/components/interfaces/Billing/Subscription/Subscription.tsx @@ -36,13 +36,13 @@ const Subscription: FC = ({ const router = useRouter() const { ref } = router.query - const enablePermissions = useFlag('enablePermissions') const projectUpdateDisabled = useFlag('disableProjectCreationAndUpdate') const canReadSubscription = checkPermissions(PermissionAction.READ, 'subscriptions') - const canUpdateSubscription = enablePermissions - ? checkPermissions(PermissionAction.BILLING_WRITE, 'stripe.subscriptions') - : ui.selectedOrganization?.is_owner + const canUpdateSubscription = checkPermissions( + PermissionAction.BILLING_WRITE, + 'stripe.subscriptions' + ) const { usage, isLoading: loadingUsage } = useProjectUsage(ref as string) @@ -112,10 +112,8 @@ const Subscription: FC = ({
Our engineers are working on a fix. - ) : !canUpdateSubscription && enablePermissions ? ( + ) : !canUpdateSubscription ? ( 'You need additional permissions to amend subscriptions' - ) : !canUpdateSubscription && !enablePermissions ? ( - 'Only the organization owner can amend subscriptions' ) : ( '' )} diff --git a/studio/components/interfaces/Organization/BillingSettings/BillingAddress/BillingAddress.tsx b/studio/components/interfaces/Organization/BillingSettings/BillingAddress/BillingAddress.tsx index cb9978efc57..010510676a2 100644 --- a/studio/components/interfaces/Organization/BillingSettings/BillingAddress/BillingAddress.tsx +++ b/studio/components/interfaces/Organization/BillingSettings/BillingAddress/BillingAddress.tsx @@ -4,7 +4,7 @@ import { Dictionary } from 'components/grid' import { Form, Input, Button, Listbox } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useStore, useFlag } from 'hooks' +import { checkPermissions, useStore } from 'hooks' import { patch } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { COUNTRIES } from './BillingAddress.constants' @@ -22,11 +22,11 @@ const BillingAddress: FC = ({ loading, address, onAddressUpdated }) => { const orgSlug = ui.selectedOrganization?.slug ?? '' const { city, country, line1, line2, postal_code, state } = address - const enablePermissions = useFlag('enablePermissions') const canReadBillingAddress = checkPermissions(PermissionAction.BILLING_READ, 'stripe.customer') - const canUpdateBillingAddress = enablePermissions - ? checkPermissions(PermissionAction.BILLING_WRITE, 'stripe.customer') - : ui.selectedOrganization?.is_owner + const canUpdateBillingAddress = checkPermissions( + PermissionAction.BILLING_WRITE, + 'stripe.customer' + ) const [isDirty, setIsDirty] = useState(false) const initialValues = { diff --git a/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx b/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx index ca1fdf60647..052285ce1fa 100644 --- a/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx +++ b/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx @@ -1,17 +1,11 @@ import { useContext, useEffect } from 'react' import { Form, Input } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { useFlag, useStore, checkPermissions } from 'hooks' +import { useStore, checkPermissions } from 'hooks' import { API_URL } from 'lib/constants' import { patch } from 'lib/common/fetch' -import { - FormActions, - FormPanel, - FormSection, - FormSectionContent, - FormSectionLabel, -} from 'components/ui/Forms' +import { FormActions, FormPanel, FormSection, FormSectionContent } from 'components/ui/Forms' import { PageContext } from 'pages/org/[slug]/settings' @@ -24,12 +18,7 @@ const BillingEmail = () => { billing_email: PageState.organization?.billing_email ?? '', } - const enablePermissions = useFlag('enablePermissions') - - const canUpdateOrganization = enablePermissions - ? checkPermissions(PermissionAction.UPDATE, 'organizations') - : ui.selectedOrganization?.is_owner - + const canUpdateOrganization = checkPermissions(PermissionAction.UPDATE, 'organizations') const canReadBillingEmail = checkPermissions(PermissionAction.READ, 'organizations') const onUpdateOrganization = async (values: any, { setSubmitting, resetForm }: any) => { diff --git a/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx b/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx index 5dc4677375e..6057c57a403 100644 --- a/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx +++ b/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx @@ -1,6 +1,6 @@ import { FC, useState, useEffect } from 'react' -import { useFlag, useStore, checkPermissions } from 'hooks' +import { useStore } from 'hooks' import { API_URL } from 'lib/constants' import { get } from 'lib/common/fetch' @@ -9,7 +9,6 @@ import CreditBalance from './CreditBalance' import PaymentMethods from './PaymentMethods' import BillingAddress from './BillingAddress/BillingAddress' import TaxID from './TaxID/TaxID' -import { PermissionAction } from '@supabase/shared-types/out/constants' import BillingEmail from './BillingEmail' interface Props { @@ -39,14 +38,6 @@ const BillingSettings: FC = ({ organization, projects = [] }) => { ? customerBalance.toString().replace('-', '') : customerBalance - const enablePermissions = useFlag('enablePermissions') - - const canUpdateOrganization = enablePermissions - ? checkPermissions(PermissionAction.UPDATE, 'organizations') - : ui.selectedOrganization?.is_owner - - const canReadBillingEmail = checkPermissions(PermissionAction.READ, 'organizations') - useEffect(() => { getCustomerProfile() getPaymentMethods() diff --git a/studio/components/interfaces/Organization/BillingSettings/PaymentMethods.tsx b/studio/components/interfaces/Organization/BillingSettings/PaymentMethods.tsx index 7b7d38b4c39..f5675c7620b 100644 --- a/studio/components/interfaces/Organization/BillingSettings/PaymentMethods.tsx +++ b/studio/components/interfaces/Organization/BillingSettings/PaymentMethods.tsx @@ -46,15 +46,14 @@ const PaymentMethods: FC = ({ const [showAddPaymentMethodModal, setShowAddPaymentMethodModal] = useState(false) const [isUpdatingPaymentMethod, setIsUpdatingPaymentMethod] = useState(false) - const enablePermissions = useFlag('enablePermissions') - const canReadPaymentMethods = checkPermissions( PermissionAction.BILLING_READ, 'stripe.payment_methods' ) - const canUpdatePaymentMethods = enablePermissions - ? checkPermissions(PermissionAction.BILLING_WRITE, 'stripe.payment_methods') - : isOwner + const canUpdatePaymentMethods = checkPermissions( + PermissionAction.BILLING_WRITE, + 'stripe.payment_methods' + ) const onConfirmMakeDefaultPaymentMethod = async () => { try { @@ -120,9 +119,7 @@ const PaymentMethods: FC = ({
{!canUpdatePaymentMethods ? (

- {enablePermissions - ? "You need additional permissions to manage this organization's payment methods" - : 'Only organization owners can update payment methods'} + You need additional permissions to manage this organization's payment methods

) : (
diff --git a/studio/components/interfaces/Organization/BillingSettings/TaxID/TaxID.tsx b/studio/components/interfaces/Organization/BillingSettings/TaxID/TaxID.tsx index 1c525608eb4..31dffe44d4f 100644 --- a/studio/components/interfaces/Organization/BillingSettings/TaxID/TaxID.tsx +++ b/studio/components/interfaces/Organization/BillingSettings/TaxID/TaxID.tsx @@ -50,12 +50,8 @@ const TaxID: FC = ({ loading, taxIds, onTaxIdsUpdated }) => { }, [taxIds]) const hasChanges = !isEqual(taxIdValues, formattedTaxIds) - - const enablePermissions = useFlag('enablePermissions') const canReadTaxIds = checkPermissions(PermissionAction.BILLING_READ, 'stripe.tax_ids') - const canUpdateTaxIds = enablePermissions - ? checkPermissions(PermissionAction.BILLING_WRITE, 'stripe.tax_ids') - : ui.selectedOrganization?.is_owner + const canUpdateTaxIds = checkPermissions(PermissionAction.BILLING_WRITE, 'stripe.tax_ids') const onUpdateTaxId = (id: string, key: string, value: string) => { const updatedTaxIds = taxIdValues.map((taxId: any) => { diff --git a/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx b/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx index 9af091fa4d2..f87ab5c6be4 100644 --- a/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx +++ b/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx @@ -3,7 +3,7 @@ import { observer } from 'mobx-react-lite' import { Form, Input } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { useFlag, useStore, checkPermissions } from 'hooks' +import { useStore, checkPermissions } from 'hooks' import { API_URL } from 'lib/constants' import { patch } from 'lib/common/fetch' @@ -24,16 +24,8 @@ const GeneralSettings = observer(() => { const formId = 'org-general-settings' const initialValues = { name: PageState.organization.name } - - const enablePermissions = useFlag('enablePermissions') - - const canUpdateOrganization = enablePermissions - ? checkPermissions(PermissionAction.UPDATE, 'organizations') - : ui.selectedOrganization?.is_owner - - const canDeleteOrganization = enablePermissions - ? checkPermissions(PermissionAction.UPDATE, 'organizations') - : ui.selectedOrganization?.is_owner + const canUpdateOrganization = checkPermissions(PermissionAction.UPDATE, 'organizations') + const canDeleteOrganization = checkPermissions(PermissionAction.UPDATE, 'organizations') const onUpdateOrganization = async (values: any, { setSubmitting, resetForm }: any) => { if (!canUpdateOrganization) { diff --git a/studio/components/interfaces/Organization/NewProject/NotOrganizationOwnerWarning.tsx b/studio/components/interfaces/Organization/NewProject/NotOrganizationOwnerWarning.tsx index d98396c9a87..a80724fb25c 100644 --- a/studio/components/interfaces/Organization/NewProject/NotOrganizationOwnerWarning.tsx +++ b/studio/components/interfaces/Organization/NewProject/NotOrganizationOwnerWarning.tsx @@ -1,12 +1,8 @@ import { IconAlertCircle } from 'ui' - -import { useFlag } from 'hooks' import InformationBox from 'components/ui/InformationBox' // [Joshen] This can just use NoPermission component i think const NotOrganizationOwnerWarning = () => { - const enablePermissions = useFlag('enablePermissions') - return (
{ description={

- {enablePermissions - ? 'Contact your organization owner or adminstrator to create a new project.' - : 'Only the organization owner can create new projects. Contact your organization owner to create a new project for this organization.'} + Contact your organization owner or adminstrator to create a new project.

} diff --git a/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx b/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx index 092a893902b..091496b3238 100644 --- a/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx +++ b/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx @@ -6,7 +6,7 @@ import * as Tooltip from '@radix-ui/react-tooltip' import { Button, Form, IconMail, Input, Modal, Select } from 'ui' import { Member, User, Role } from 'types' -import { checkPermissions, useFlag, useOrganizationDetail, useStore } from 'hooks' +import { checkPermissions, useOrganizationDetail, useStore } from 'hooks' import { post } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { PermissionAction } from '@supabase/shared-types/out/constants' @@ -23,8 +23,6 @@ const InviteMemberButton: FC = ({ user, members = [], roles = [], rolesAd const router = useRouter() const { slug } = router.query - const enablePermissions = useFlag('enablePermissions') - const [isOpen, setIsOpen] = useState(false) const { mutateOrgMembers } = useOrganizationDetail((slug as string) || '') @@ -57,16 +55,14 @@ const InviteMemberButton: FC = ({ user, members = [], roles = [], rolesAd } } - const roleId = enablePermissions - ? Number(values.role) - : roles.find((role) => role.name === 'Developer')?.id ?? roles[0].id + const roleId = Number(values.role) setSubmitting(true) const response = await post(`${API_URL}/organizations/${slug}/members/invite`, { invited_email: values.email.toLowerCase(), owner_id: user.id, - ...(enablePermissions ? { role_id: roleId } : {}), + role_id: roleId, }) if (response.error) { @@ -141,9 +137,7 @@ const InviteMemberButton: FC = ({ user, members = [], roles = [], rolesAd }, [roles]) const selectedRole = roles.find((role) => role.id === Number(values.role)) - const invalidRoleSelected = enablePermissions - ? values.role && !rolesAddable.includes(Number(values.role)) - : false + const invalidRoleSelected = values.role && !rolesAddable.includes(Number(values.role)) return ( <> @@ -151,7 +145,7 @@ const InviteMemberButton: FC = ({ user, members = [], roles = [], rolesAd
- {roles && enablePermissions && ( + {roles && (