mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
refactor: move storage docs into content directory (#21129)
This commit is contained in:
1 parent
b683b43b81
commit
df92331a3b
22 files changed
+253
-324
No files matched your search
@@ -1,12 +1,11 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage',
|
||||
title: 'Storage',
|
||||
description: 'Use Supabase to store and serve files.',
|
||||
subtitle: 'Use Supabase to store and serve files.',
|
||||
sidebar_label: 'Overview',
|
||||
}
|
||||
---
|
||||
id: 'storage'
|
||||
title: 'Storage'
|
||||
description: 'Use Supabase to store and serve files.'
|
||||
subtitle: 'Use Supabase to store and serve files.'
|
||||
sidebar_label: 'Overview'
|
||||
hideToc: true
|
||||
---
|
||||
|
||||
Supabase Storage makes it simple to upload and serve files of any size, providing a robust framework for file access controls.
|
||||
|
||||
@@ -44,7 +43,18 @@ export const examples = [
|
||||
Find the source code and documentation in the Supabase GitHub repository.
|
||||
|
||||
<div className="grid md:grid-cols-12 gap-4 not-prose">
|
||||
{integrations.map((x) => (
|
||||
{[
|
||||
{
|
||||
name: 'Supabase Storage API',
|
||||
description: 'View the source code.',
|
||||
href: 'https://github.com/supabase/storage-api',
|
||||
},
|
||||
{
|
||||
name: 'OpenAPI Spec',
|
||||
description: 'See the Swagger Documentation for Supabase Storage.',
|
||||
href: 'https://supabase.github.io/storage/',
|
||||
},
|
||||
].map((x) => (
|
||||
<div className="col-span-6" key={x.href}>
|
||||
<Link href={x.href} passHref>
|
||||
<GlassPanel title={x.name}>{x.description}</GlassPanel>
|
||||
@@ -52,20 +62,3 @@ Find the source code and documentation in the Supabase GitHub repository.
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
export const integrations = [
|
||||
{
|
||||
name: 'Supabase Storage API',
|
||||
description: 'View the source code.',
|
||||
href: 'https://github.com/supabase/storage-api',
|
||||
},
|
||||
{
|
||||
name: 'OpenAPI Spec',
|
||||
description: 'See the Swagger Documentation for Supabase Storage.',
|
||||
href: 'https://supabase.github.io/storage/',
|
||||
},
|
||||
]
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} hideToc={true} />
|
||||
|
||||
export default Page
|
||||
+6
-12
@@ -1,11 +1,9 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-creating-buckets',
|
||||
title: 'Creating Buckets',
|
||||
description: 'Learn how to create Supabase Storage buckets.',
|
||||
sidebar_label: 'Buckets',
|
||||
}
|
||||
---
|
||||
id: 'storage-creating-buckets'
|
||||
title: 'Creating Buckets'
|
||||
description: 'Learn how to create Supabase Storage buckets.'
|
||||
sidebar_label: 'Buckets'
|
||||
---
|
||||
|
||||
You can create a bucket using the Supabase Dashboard. Since storage is interoperable with your Postgres database, you can also use SQL or our client libraries.
|
||||
Here we create a bucket called "avatars":
|
||||
@@ -87,7 +85,3 @@ const { data, error } = await supabase.storage.createBucket('avatars', {
|
||||
If an upload request doesn't meet the above restrictions it will be rejected.
|
||||
|
||||
For more information check [File Limits](/docs/guides/storage/uploads/file-limits) Section.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+6
-12
@@ -1,11 +1,9 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-bucket-public-and-private',
|
||||
title: 'Storage Buckets',
|
||||
description: 'Learn how Supabase Storage Buckets works.',
|
||||
sidebar_label: 'Buckets',
|
||||
}
|
||||
---
|
||||
id: 'storage-bucket-public-and-private'
|
||||
title: 'Storage Buckets'
|
||||
description: 'Learn how Supabase Storage Buckets works.'
|
||||
sidebar_label: 'Buckets'
|
||||
---
|
||||
|
||||
Buckets allow you to keep your files organized and determines the [Access Model](#access-model) for your assets. [Upload restrictions](/docs/guides/storage/buckets/creating-buckets#restricting-uploads) like max file size and allowed content types are also defined at the bucket level.
|
||||
|
||||
@@ -40,7 +38,3 @@ Access control is still enforced for other types of operations including uploadi
|
||||
- Blog post content
|
||||
|
||||
Public buckets are more performant than private buckets since they are [cached differently](/docs/guides/storage/cdn/fundamentals#public-vs-private-buckets).
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+6
-12
@@ -1,11 +1,9 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-cdn',
|
||||
title: 'Storage CDN',
|
||||
description: 'Learn how Supabase Storage caches objects with a CDN.',
|
||||
sidebar_label: 'CDN',
|
||||
}
|
||||
---
|
||||
id: 'storage-cdn'
|
||||
title: 'Storage CDN'
|
||||
description: 'Learn how Supabase Storage caches objects with a CDN.'
|
||||
sidebar_label: 'CDN'
|
||||
---
|
||||
|
||||
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 cache content from an **origin server**. For Supabase Storage, the origin is the storage server running in the [same region as your project](https://supabase.com/dashboard/project/_/settings/general). Aside from performance, CDNs also help with security and availability by mitigating Distributed Denial of Service (DDoS) and other application attacks.
|
||||
|
||||
@@ -35,7 +33,3 @@ Objects in public buckets do not require any authorization to access objects. Th
|
||||
|
||||
For private buckets, permissions for accessing each object is checked on a per user level. For example, if two different users access the same object in a private bucket from the same region, it results in a cache miss for both the users since they might have different security policies attached to them.
|
||||
On the other hand, if two different users access the same object in a public bucket from the same region, it results in a cache hit for the second user.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+6
-12
@@ -1,11 +1,9 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-cdn',
|
||||
title: 'Cache Metrics',
|
||||
description: 'Learn how Supabase Storage caches objects with a CDN.',
|
||||
sidebar_label: 'CDN',
|
||||
}
|
||||
---
|
||||
id: 'storage-cdn'
|
||||
title: 'Cache Metrics'
|
||||
description: 'Learn how Supabase Storage caches objects with a CDN.'
|
||||
sidebar_label: 'CDN'
|
||||
---
|
||||
|
||||
Cache hits can be determined via the `metadata.response.headers.cf_cache_status` key in our [Logs Explorer](/docs/guides/platform/logs#logs-explorer). Any value that corresponds to either `HIT`, `STALE`, `REVALIDATED`, or `UPDATING` is categorized as a cache hit.
|
||||
The following example query will show the top cache misses from the `edge_logs`:
|
||||
@@ -50,7 +48,3 @@ order by timestamp desc;
|
||||
```
|
||||
|
||||
Try out [this query](https://supabase.com/dashboard/project/_/logs/explorer?q=%0Aselect%0A++timestamp_trunc%28timestamp%2C+hour%29+as+timestamp%2C%0A++countif%28h.cf_cache_status+in+%28%27HIT%27%2C+%27STALE%27%2C+%27REVALIDATED%27%2C+%27UPDATING%27%29%29+%2F+count%28f.id%29+as+ratio%0Afrom%0A++edge_logs+as+f%0A++cross+join+unnest%28f.metadata%29+as+m%0A++cross+join+unnest%28m.request%29+as+r%0A++cross+join+unnest%28m.response%29+as+res%0A++cross+join+unnest%28res.headers%29+as+h%0Awhere+starts_with%28r.path%2C+%27%2Fstorage%2Fv1%2Fobject%27%29+and+r.method+%3D+%27GET%27%0Agroup+by+timestamp%0Aorder+by+timestamp+desc%3B) in the Logs Explorer.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+6
-12
@@ -1,11 +1,9 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-cdn',
|
||||
title: 'Smart CDN',
|
||||
description: 'Learn how Supabase Storage caches objects with a CDN.',
|
||||
sidebar_label: 'CDN',
|
||||
}
|
||||
---
|
||||
id: 'storage-cdn'
|
||||
title: 'Smart CDN'
|
||||
description: 'Learn how Supabase Storage caches objects with a CDN.'
|
||||
sidebar_label: 'CDN'
|
||||
---
|
||||
|
||||
With Smart CDN caching enabled, the asset metadata in your database is synchronized to the edge. This automatically revalidates the cache when the asset is changed or deleted.
|
||||
|
||||
@@ -38,7 +36,3 @@ If you anticipate that your asset might be deleted, it's advisable to set a shor
|
||||
If you need to ensure assets refresh directly from the origin server and bypass the cache, you can achieve this by adding a unique query string to the URL.
|
||||
|
||||
For instance, you can use a URL like `/storage/v1/object/sign/profile-pictures/cat.jpg?version=1` with a long browser cache (e.g., 1 year). To update the picture, increment the version query parameter in the URL, like `/storage/v1/object/sign/profile-pictures/cat.jpg?version=2`. The CDN will recognize it as a new object and fetch the updated version from the origin.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+6
-12
@@ -1,11 +1,9 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-errors-codes',
|
||||
title: 'Error Codes',
|
||||
description: 'Supabase Error Codes',
|
||||
sidebar_label: 'Debugging',
|
||||
}
|
||||
---
|
||||
id: 'storage-errors-codes'
|
||||
title: 'Error Codes'
|
||||
description: 'Supabase Error Codes'
|
||||
sidebar_label: 'Debugging'
|
||||
---
|
||||
|
||||
Here's a list of the most common error codes and their potential resolutions:
|
||||
|
||||
@@ -57,7 +55,3 @@ This issue occurs where there is a unhandled error.
|
||||
**Resolution:**
|
||||
|
||||
- File a support ticket to Storage team [here](https://supabase.com/dashboard/support/new)
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+6
-12
@@ -1,11 +1,9 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-logs',
|
||||
title: 'Logs',
|
||||
description: 'Learn how to check Storage Logs',
|
||||
sidebar_label: 'Debugging',
|
||||
}
|
||||
---
|
||||
id: 'storage-logs'
|
||||
title: 'Logs'
|
||||
description: 'Learn how to check Storage Logs'
|
||||
sidebar_label: 'Debugging'
|
||||
---
|
||||
|
||||
Accessing the [Storage Logs](https://supabase.green/dashboard/project/__/logs/explorer?q=select+id%2C+storage_logs.timestamp%2C+event_message+from+storage_logs%0A++%0A++order+by+timestamp+desc%0A++limit+100%0A++) allows you to examine all incoming request logs to your Storage service. You can also filter logs and delve into specific aspects of your requests.
|
||||
|
||||
@@ -62,7 +60,3 @@ where r.remoteAddress in ("IP_ADDRESS")
|
||||
order by timestamp desc
|
||||
limit 100;
|
||||
```
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+7
-13
@@ -1,12 +1,10 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-schema-optimizations',
|
||||
title: 'Storage Optimizations',
|
||||
description: 'Scaling Storage',
|
||||
subtitle: 'Scaling Storage',
|
||||
sidebar_label: 'Schema',
|
||||
}
|
||||
---
|
||||
id: 'storage-schema-optimizations'
|
||||
title: 'Storage Optimizations'
|
||||
description: 'Scaling Storage'
|
||||
subtitle: 'Scaling Storage'
|
||||
sidebar_label: 'Schema'
|
||||
---
|
||||
|
||||
Here are some optimizations that you can consider to improve performance and reduce costs as you start scaling Storage.
|
||||
|
||||
@@ -86,7 +84,3 @@ const { data, error } = await supabase.rpc('list_objects', {
|
||||
## Optimizing RLS
|
||||
|
||||
When creating RLS policies against the storage tables you can add indexes to the interested columns to speed up the lookup
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+8
-14
@@ -1,13 +1,11 @@
|
||||
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.',
|
||||
subtitle: 'Learn how to use Supabase to store and serve files.',
|
||||
sidebar_label: 'Quickstart',
|
||||
tocVideo: 'J9mTPY8rIXE',
|
||||
}
|
||||
---
|
||||
id: 'storage-quickstart'
|
||||
title: 'Storage Quickstart'
|
||||
description: 'Learn how to use Supabase to store and serve files.'
|
||||
subtitle: 'Learn how to use Supabase to store and serve files.'
|
||||
sidebar_label: 'Quickstart'
|
||||
tocVideo: 'J9mTPY8rIXE'
|
||||
---
|
||||
|
||||
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-user-management).
|
||||
|
||||
@@ -238,7 +236,3 @@ create policy "Public Access"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+7
-13
@@ -1,12 +1,10 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-schema-design',
|
||||
title: 'The Storage Schema',
|
||||
description: 'Learn about the storage schema',
|
||||
subtitle: 'Learn about the storage schema',
|
||||
sidebar_label: 'Schema',
|
||||
}
|
||||
---
|
||||
id: 'storage-schema-design'
|
||||
title: 'The Storage Schema'
|
||||
description: 'Learn about the storage schema'
|
||||
subtitle: 'Learn about the storage schema'
|
||||
sidebar_label: 'Schema'
|
||||
---
|
||||
|
||||
Storage uses Postgres to store metadata regarding your buckets and objects. Users can use RLS (Row-Level Security) policies for access control. This data is stored in a dedicated schema within your project called `storage`.
|
||||
|
||||
@@ -28,7 +26,3 @@ You have the option to query this table directly to retrieve information about y
|
||||
We strongly recommend refraining from making any alterations to the `storage` schema and treating it as read-only. This approach is important because any modifications to the schema on your end could potentially clash with our future updates, leading to downtime.
|
||||
|
||||
However, we encourage you to add custom indexes as they can significantly improve the performance of the RLS policies you create for enforcing access control.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+7
-13
@@ -1,12 +1,10 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-schema',
|
||||
title: 'Storage Helper Functions',
|
||||
description: 'Learn the storage schema',
|
||||
subtitle: 'Learn the storage schema',
|
||||
sidebar_label: 'Schema',
|
||||
}
|
||||
---
|
||||
id: 'storage-schema'
|
||||
title: 'Storage Helper Functions'
|
||||
description: 'Learn the storage schema'
|
||||
subtitle: 'Learn the storage schema'
|
||||
sidebar_label: 'Schema'
|
||||
---
|
||||
|
||||
Supabase Storage provides SQL helper functions which you can use to write RLS policies.
|
||||
|
||||
@@ -63,7 +61,3 @@ with check (
|
||||
bucket_id = 'cats' and storage.extension(name) = 'png'
|
||||
);
|
||||
```
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+7
-13
@@ -1,12 +1,10 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-access-control',
|
||||
title: 'Storage Access Control',
|
||||
description: 'Learn how to restrict Supabase file uploads.',
|
||||
sidebar_label: 'Uploads',
|
||||
tocVideo: '4ERX__Y908k',
|
||||
}
|
||||
---
|
||||
id: 'storage-access-control'
|
||||
title: 'Storage Access Control'
|
||||
description: 'Learn how to restrict Supabase file uploads.'
|
||||
sidebar_label: 'Uploads'
|
||||
tocVideo: '4ERX__Y908k'
|
||||
---
|
||||
|
||||
Supabase Storage is designed to work perfectly with Postgres [Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security) (RLS).
|
||||
|
||||
@@ -103,7 +101,3 @@ using ( auth.uid() = owner_id );
|
||||
If you exclusively use Storage from trusted clients, such as your own servers, and need to bypass the RLS policies, you can use the `service key` in the `Authorization` header. Service keys entirely bypass RLS policies, granting you unrestricted access to all Storage APIs.
|
||||
|
||||
Remember you should not share the service key publicly.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+8
-14
@@ -1,13 +1,11 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-image-transformations',
|
||||
title: 'Serving assets from Storage',
|
||||
description: 'Serving assets from Storage',
|
||||
subtitle: 'Serving assets from Storage',
|
||||
sidebar_label: 'Serving assets',
|
||||
tocVideo: 'dLqSmxX3r7I',
|
||||
}
|
||||
---
|
||||
id: 'storage-image-transformations'
|
||||
title: 'Serving assets from Storage'
|
||||
description: 'Serving assets from Storage'
|
||||
subtitle: 'Serving assets from Storage'
|
||||
sidebar_label: 'Serving assets'
|
||||
tocVideo: 'dLqSmxX3r7I'
|
||||
---
|
||||
|
||||
## Public buckets
|
||||
|
||||
@@ -53,7 +51,3 @@ if (data) {
|
||||
console.log(data.signedUrl)
|
||||
}
|
||||
```
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+8
-14
@@ -1,13 +1,11 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-image-transformations',
|
||||
title: 'Storage Image Transformations',
|
||||
description: 'Transform images with Storage',
|
||||
subtitle: 'Transform images with Storage',
|
||||
sidebar_label: 'Image Transformations',
|
||||
tocVideo: 'dLqSmxX3r7I',
|
||||
}
|
||||
---
|
||||
id: 'storage-image-transformations'
|
||||
title: 'Storage Image Transformations'
|
||||
description: 'Transform images with Storage'
|
||||
subtitle: 'Transform images with Storage'
|
||||
sidebar_label: 'Image Transformations'
|
||||
tocVideo: 'dLqSmxX3r7I'
|
||||
---
|
||||
|
||||
Supabase Storage offers the functionality to optimize and resize images on the fly. Any image stored in your buckets can be transformed and optimized for fast delivery.
|
||||
|
||||
@@ -383,7 +381,3 @@ IMGPROXY_URL=yourinternalimgproxyurl.internal.com
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+6
-12
@@ -1,11 +1,9 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-file-limits',
|
||||
title: 'Limits',
|
||||
description: 'Learn how to increase Supabase file limits.',
|
||||
sidebar_label: 'Uploads',
|
||||
}
|
||||
---
|
||||
id: 'storage-file-limits'
|
||||
title: 'Limits'
|
||||
description: 'Learn how to increase Supabase file limits.'
|
||||
sidebar_label: 'Uploads'
|
||||
---
|
||||
|
||||
You can customize the Max file size for file uploads.
|
||||
|
||||
@@ -35,7 +33,3 @@ You can have different restrictions on a per bucket level.
|
||||
You are able to restrict the file types (eg. `pdf`, `images`, `videos`) along with the max file size which should be lower than the global limit.
|
||||
|
||||
To apply these limit on a bucket level see [Creating Bucket](/docs/guides/storage/buckets/creating-buckets#restricting-uploads)
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+7
-13
@@ -1,12 +1,10 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'resumable-uploads',
|
||||
title: 'Resumable Uploads',
|
||||
description: 'Learn how to upload files to Supabase Storage.',
|
||||
subtitle: 'Learn how to upload files to Supabase Storage.',
|
||||
sidebar_label: 'Uploads',
|
||||
}
|
||||
---
|
||||
id: 'resumable-uploads'
|
||||
title: 'Resumable Uploads'
|
||||
description: 'Learn how to upload files to Supabase Storage.'
|
||||
subtitle: 'Learn how to upload files to Supabase Storage.'
|
||||
sidebar_label: 'Uploads'
|
||||
---
|
||||
|
||||
The resumable upload method is recommended when:
|
||||
|
||||
@@ -149,7 +147,3 @@ We do advise against overwriting files when possible, as the CDN will take some
|
||||
Uploading a file to a new path is the recommended way to avoid propagation delays and stale content.
|
||||
|
||||
To learn more, see the [CDN](/docs/guides/storage/cdn/fundamentals) guide.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+7
-13
@@ -1,12 +1,10 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'standard-uploads',
|
||||
title: 'Standard Uploads',
|
||||
description: 'Learn how to upload files to Supabase Storage.',
|
||||
subtitle: 'Learn how to upload files to Supabase Storage.',
|
||||
sidebar_label: 'Uploads',
|
||||
}
|
||||
---
|
||||
id: 'standard-uploads'
|
||||
title: 'Standard Uploads'
|
||||
description: 'Learn how to upload files to Supabase Storage.'
|
||||
subtitle: 'Learn how to upload files to Supabase Storage.'
|
||||
sidebar_label: 'Uploads'
|
||||
---
|
||||
|
||||
## Uploading
|
||||
|
||||
@@ -125,7 +123,3 @@ await supabase.storage.from('bucket_name').upload('file_path', file, {
|
||||
|
||||
When two or more clients upload a file to the same path, the first client to complete the upload will succeed and the other clients will receive a `400 Asset Already Exists` error.
|
||||
If you provide the `x-upsert` header the last client to complete the upload will succeed instead.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+85
-3
@@ -1,8 +1,17 @@
|
||||
import { dirname, join } from 'node:path'
|
||||
import { type CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx'
|
||||
import matter from 'gray-matter'
|
||||
import { serialize } from 'next-mdx-remote/serialize'
|
||||
import type { SerializeOptions } from 'next-mdx-remote/dist/types'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { readdir, readFile } from 'node:fs/promises'
|
||||
import { dirname, join, extname, sep } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
|
||||
export const DOCS_DIRECTORY = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
export const GUIDES_DIRECTORY = join(DOCS_DIRECTORY, 'content/guides')
|
||||
import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' }
|
||||
|
||||
const DOCS_DIRECTORY = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const GUIDES_DIRECTORY = join(DOCS_DIRECTORY, 'content/guides')
|
||||
|
||||
type GuideFrontmatter = {
|
||||
title: string
|
||||
@@ -15,3 +24,76 @@ export function isValidGuideFrontmatter(obj: object): obj is GuideFrontmatter {
|
||||
if ('description' in obj && typeof obj.description !== 'string') return false
|
||||
return true
|
||||
}
|
||||
|
||||
export async function getGuidesStaticPaths(section: string) {
|
||||
const directory = join(GUIDES_DIRECTORY, section)
|
||||
const files = (await readdir(directory, { recursive: true }))
|
||||
.filter((file) => extname(file) === '.mdx')
|
||||
.map((file) => ({
|
||||
params: {
|
||||
slug: file.replace(/\.mdx$/, '').split(sep),
|
||||
},
|
||||
}))
|
||||
|
||||
// Index page isn't included in the directory
|
||||
const indexFile = join(GUIDES_DIRECTORY, `${section}.mdx`)
|
||||
if (existsSync(indexFile)) {
|
||||
files.push({ params: { slug: [] } })
|
||||
}
|
||||
|
||||
return {
|
||||
paths: files,
|
||||
fallback: false,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getGuidesStaticProps(
|
||||
section: string,
|
||||
{ params }: { params?: { slug?: string | Array<string> } }
|
||||
) {
|
||||
let relPath: string
|
||||
switch (typeof params?.slug) {
|
||||
case 'string':
|
||||
relPath = section + sep + params.slug
|
||||
break
|
||||
case 'object': // actually an array
|
||||
relPath = section + sep + params.slug.join(sep)
|
||||
break
|
||||
case 'undefined':
|
||||
relPath = section
|
||||
}
|
||||
|
||||
const fullPath = join(GUIDES_DIRECTORY, relPath + '.mdx')
|
||||
const mdx = await readFile(fullPath, 'utf-8')
|
||||
|
||||
const editLink = `supabase/supabase/blob/master/apps/docs/content/guides/${relPath}.mdx`
|
||||
|
||||
const { data: frontmatter, content } = matter(mdx)
|
||||
if (!isValidGuideFrontmatter(frontmatter)) {
|
||||
throw Error('Type of frontmatter is not valid')
|
||||
}
|
||||
|
||||
const codeHikeOptions: CodeHikeConfig = {
|
||||
theme: codeHikeTheme,
|
||||
lineNumbers: true,
|
||||
showCopyButton: true,
|
||||
skipLanguages: [],
|
||||
autoImport: false,
|
||||
}
|
||||
|
||||
const mdxOptions: SerializeOptions = {
|
||||
mdxOptions: {
|
||||
useDynamicImport: true,
|
||||
remarkPlugins: [remarkGfm, [remarkCodeHike, codeHikeOptions]],
|
||||
},
|
||||
}
|
||||
const mdxSource = await serialize(content, mdxOptions)
|
||||
|
||||
return {
|
||||
props: {
|
||||
frontmatter,
|
||||
mdxSource,
|
||||
editLink,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,88 +1,16 @@
|
||||
import { type CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx'
|
||||
import matter from 'gray-matter'
|
||||
import { serialize } from 'next-mdx-remote/serialize'
|
||||
import type { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next'
|
||||
import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next'
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
import type { SerializeOptions } from 'next-mdx-remote/dist/types'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { readdir, readFile } from 'node:fs/promises'
|
||||
import { join, extname, sep } from 'node:path'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
|
||||
import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' }
|
||||
|
||||
import components from '~/components'
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
import { GUIDES_DIRECTORY, isValidGuideFrontmatter } from '~/lib/docs'
|
||||
import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs'
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
const directory = join(GUIDES_DIRECTORY, 'auth')
|
||||
const files = (await readdir(directory, { recursive: true }))
|
||||
.filter((file) => extname(file) === '.mdx')
|
||||
.map((file) => ({
|
||||
params: {
|
||||
slug: file.replace(/\.mdx$/, '').split(sep),
|
||||
},
|
||||
}))
|
||||
|
||||
// Index page isn't included in the directory
|
||||
const indexFile = join(GUIDES_DIRECTORY, 'auth.mdx')
|
||||
if (existsSync(indexFile)) {
|
||||
files.push({ params: { slug: [] } })
|
||||
}
|
||||
|
||||
return {
|
||||
paths: files,
|
||||
fallback: false,
|
||||
}
|
||||
return getGuidesStaticPaths('auth')
|
||||
}) satisfies GetStaticPaths
|
||||
|
||||
export const getStaticProps = (async ({ params }) => {
|
||||
let relPath: string
|
||||
switch (typeof params.slug) {
|
||||
case 'string':
|
||||
relPath = 'auth' + sep + params.slug
|
||||
break
|
||||
case 'object': // actually an array
|
||||
relPath = 'auth' + sep + params.slug.join(sep)
|
||||
break
|
||||
case 'undefined':
|
||||
relPath = 'auth'
|
||||
}
|
||||
|
||||
const fullPath = join(GUIDES_DIRECTORY, relPath + '.mdx')
|
||||
const mdx = await readFile(fullPath, 'utf-8')
|
||||
|
||||
const editLink = `supabase/supabase/blob/master/apps/docs/content/guides/${relPath}.mdx`
|
||||
|
||||
const { data: frontmatter, content } = matter(mdx)
|
||||
if (!isValidGuideFrontmatter(frontmatter)) {
|
||||
throw Error('Type of frontmatter is not valid')
|
||||
}
|
||||
|
||||
const codeHikeOptions: CodeHikeConfig = {
|
||||
theme: codeHikeTheme,
|
||||
lineNumbers: true,
|
||||
showCopyButton: true,
|
||||
skipLanguages: [],
|
||||
autoImport: false,
|
||||
}
|
||||
|
||||
const mdxOptions: SerializeOptions = {
|
||||
mdxOptions: {
|
||||
useDynamicImport: true,
|
||||
remarkPlugins: [remarkGfm, [remarkCodeHike, codeHikeOptions]],
|
||||
},
|
||||
}
|
||||
const mdxSource = await serialize(content, mdxOptions)
|
||||
|
||||
return {
|
||||
props: {
|
||||
frontmatter,
|
||||
mdxSource,
|
||||
editLink,
|
||||
},
|
||||
}
|
||||
export const getStaticProps = (async (args) => {
|
||||
return getGuidesStaticProps('auth', args)
|
||||
}) satisfies GetStaticProps
|
||||
|
||||
export default function AuthGuide({
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { type GetStaticPaths, type GetStaticProps, type InferGetStaticPropsType } from 'next'
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
|
||||
import components from '~/components'
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
import { getGuidesStaticPaths, getGuidesStaticProps } from '~/lib/docs'
|
||||
|
||||
export const getStaticPaths = (async () => {
|
||||
return getGuidesStaticPaths('storage')
|
||||
}) satisfies GetStaticPaths
|
||||
|
||||
export const getStaticProps = (async (args) => {
|
||||
return getGuidesStaticProps('storage', args)
|
||||
}) satisfies GetStaticProps
|
||||
|
||||
export default function StorageGuide({
|
||||
frontmatter,
|
||||
mdxSource,
|
||||
editLink,
|
||||
}: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
const { hideToc, ...meta } = frontmatter
|
||||
|
||||
return (
|
||||
<Layout meta={meta} hideToc={hideToc} editLink={editLink}>
|
||||
<MDXRemote {...mdxSource} components={components} />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
* Also deletes import and export statements.
|
||||
*/
|
||||
|
||||
let SUB_DIR = null
|
||||
let SUB_DIR = 'storage'
|
||||
|
||||
import { parse } from 'acorn'
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown'
|
||||
|
||||
Reference in new issue
Block a user