refactor: move storage docs into content directory (#21129)

This commit is contained in:
Charis authored and GitHub committed 2024-02-14 13:28:39 +00:00
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
View File
@@ -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,
},
}
}
+5 -77
View File
@@ -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>
)
}
+1 -1
View File
@@ -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'