This commit is contained in:
Kevin Grüneberg committed 2024-07-20 14:04:16 +08:00
1 parent 31c3ba2bc5
commit ae3eecc9aa
17 files changed
+5365 -128

No files matched your search

+19
View File
@@ -0,0 +1,19 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
type Params = { slug: string[] }
const GuidePage = async ({ params }: { params: Params }) => {
const data = await getGuidesMarkdown(params)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams()
const generateMetadata = genGuideMeta(getGuidesMarkdown)
export default GuidePage
export { generateStaticParams, generateMetadata }
@@ -0,0 +1,134 @@
import { type SerializeOptions } from 'next-mdx-remote/dist/types'
import { redirect } from 'next/navigation'
import { relative } from 'path'
import rehypeSlug from 'rehype-slug'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { notFoundLink } from '~/features/recommendations/NotFound.utils'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
// We fetch these docs at build time from an external repo
const org = 'supabase'
const repo = 'vecs'
const branch = 'main'
const docsDir = 'docs'
const externalSite = 'https://supabase.github.io/vecs'
// Each external docs page is mapped to a local page
const pageMap = [
{
slug: 'api',
meta: {
title: 'API',
},
remoteFile: 'api.md',
},
{
slug: 'collections',
meta: {
title: 'Collections',
},
remoteFile: 'concepts_collections.md',
},
{
slug: 'indexes',
meta: {
title: 'Indexes',
},
remoteFile: 'concepts_indexes.md',
},
{
slug: 'metadata',
meta: {
title: 'Metadata',
},
remoteFile: 'concepts_metadata.md',
},
]
interface Params {
slug: string
}
const PythonClientDocs = async ({ params }: { params: Params }) => {
const { meta, ...data } = await getContent(params)
const options = {
mdxOptions: {
remarkPlugins: [remarkMkDocsAdmonition, [removeTitle, meta.title]],
rehypePlugins: [[linkTransform, urlTransform], rehypeSlug],
},
} as SerializeOptions
return <GuideTemplate mdxOptions={options} meta={meta} {...data} />
}
/**
* Fetch markdown from external repo
*/
const getContent = async ({ slug }: Params) => {
const page = pageMap.find(({ slug: validSlug }) => validSlug && validSlug === slug)
if (!page) {
redirect(notFoundLink(`api/python/${slug}`))
}
const { remoteFile, meta } = page
const editLink = newEditLink(`${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}`)
const response = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/${remoteFile}`
)
const content = await response.text()
return {
pathname: `/guides/ai/python/${slug}` satisfies `/${string}`,
meta,
content,
editLink,
}
}
const urlTransform: UrlTransformFunction = (url) => {
try {
const externalSiteUrl = new URL(externalSite)
const placeholderHostname = 'placeholder'
const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`)
// Don't modify a url with a FQDN or a url that's only a hash
if (hostname !== placeholderHostname || pathname === '/') {
return url
}
const relativePage = (
pathname.endsWith('.md')
? pathname.replace(/\.md$/, '')
: relative(externalSiteUrl.pathname, pathname)
).replace(/^\//, '')
const page = pageMap.find(({ remoteFile }) => `${relativePage}.md` === remoteFile)
// If we have a mapping for this page, use the mapped path
if (page) {
return page.slug + hash
}
// If we don't have this page in our docs, link to original docs
return `${externalSite}/${relativePage}${hash}`
} catch (err) {
console.error('Error transforming markdown URL', err)
return url
}
}
const generateStaticParams = () => pageMap.map(({ slug }) => ({ slug }))
const generateMetadata = genGuideMeta(getContent)
export default PythonClientDocs
export { generateStaticParams, generateMetadata }
+106
View File
@@ -0,0 +1,106 @@
import ReactMarkdown from 'react-markdown'
import { CodeBlock } from 'ui'
import { Heading } from 'ui/src/components/CustomHTMLElements'
import { type TOCHeader } from '~/components/GuidesTableOfContents'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import type { Parameter } from '~/lib/refGenerator/refTypes'
import specFile from '~/spec/cli_v1_config.yaml' assert { type: 'yml' }
const meta = {
title: 'Supabase CLI config',
}
const generateMetadata = genGuideMeta(() => ({
pathname: '/guides/cli/config',
meta,
}))
const tocList: TOCHeader[] = []
const content = specFile.info.tags.map((tag: { id: string; title: string }, id: number) => {
tocList.push({ id: `${id}`, text: tag.title, link: `${tag.id}-config`, level: 2 })
return (
<div>
<Heading tag="h2">{tag.title} Config</Heading>
{specFile.parameters
.filter((param: Parameter) => param.tags && param.tags[0] === tag.id)
.map((parameter: Parameter, id: number) => {
tocList.push({ id: `${id}`, text: parameter.id, link: `#${parameter.id}`, level: 3 })
return <Info parameter={parameter} />
})}
</div>
)
})
const Config = () => {
const editLink = newEditLink('supabase/supabase/blob/master/apps/docs/spec/cli_v1_config.yaml')
return (
<GuideTemplate meta={meta} editLink={editLink}>
<ReactMarkdown>{specFile.info.description}</ReactMarkdown>
<div>{content}</div>
</GuideTemplate>
)
}
function Info({ parameter }: { parameter: Parameter }) {
return (
<div className="mt-8">
<div>
<Heading tag="h3" parseAnchors={false} customAnchor={parameter.id}>
<code>{parameter.title}</code>
</Heading>
<div className="border-b pb-8" key={parameter.id}>
<div className=" mb-16">
<div>
<table className="table-auto">
<thead>
<tr>
<th className="text-left">Name</th>
<th className="text-left">Default</th>
<th className="text-left">Required</th>
</tr>
</thead>
<tbody>
<tr>
<td>{parameter.id}</td>
<td>{parameter.default ? parameter.default.toString() : 'None'}</td>
<td>{parameter.required !== undefined && parameter.required.toString()}</td>
</tr>
</tbody>
</table>
</div>
<div className="mb-4 scroll-mt-16 mt-0 ">
<p className="text-sm font-bold uppercase">Description</p>
<ReactMarkdown>{parameter.description}</ReactMarkdown>
</div>
{parameter.usage && (
<div className="mb-4 scroll-mt-16 mt-0 ">
<p className="text-sm font-bold uppercase">Usage</p>
<CodeBlock className="useless-code-block-class" language="py">
{parameter.usage}
</CodeBlock>
</div>
)}
{parameter.links && (
<div>
<p className="text-sm font-bold uppercase">See also</p>
<ul>
{parameter.links.map((link) => (
<li key={link.link}>
<a href={link.link}>{link.name}</a>
</li>
))}
</ul>
</div>
)}
</div>
</div>
</div>
</div>
)
}
export default Config
export { generateMetadata }
@@ -0,0 +1,134 @@
import { type SerializeOptions } from 'next-mdx-remote/dist/types'
import { redirect } from 'next/navigation'
import { relative } from 'node:path'
import rehypeSlug from 'rehype-slug'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { notFoundLink } from '~/features/recommendations/NotFound.utils'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
// We fetch these docs at build time from an external repo
const org = 'supabase'
const repo = 'setup-cli'
const branch = 'gh-pages'
const docsDir = 'docs'
const externalSite = 'https://supabase.github.io/setup-cli'
// Each external docs page is mapped to a local page
const pageMap = [
{
slug: 'generating-types',
meta: {
title: 'Generate types using GitHub Actions',
description: 'End-to-end type safety across client, server, and database.',
subtitle: 'End-to-end type safety across client, server, and database.',
tocVideo: 'VSNgAIObBdw',
},
remoteFile: 'generating-types.md',
},
{
slug: 'testing',
meta: {
title: 'Automated testing using GitHub Actions',
description: 'Run your tests when you or your team make changes.',
subtitle: 'Run your tests when you or your team make changes.',
},
remoteFile: 'testing.md',
},
{
slug: 'backups',
meta: {
title: 'Automated backups using GitHub Actions',
description: 'Backup your database on a regular basis.',
subtitle: 'Backup your database on a regular basis.',
},
remoteFile: 'backups.md',
},
]
type Params = { slug: string }
const ActionDocs = async ({ params }: { params: Params }) => {
const { meta, ...data } = await getContent(params)
const options = {
mdxOptions: {
remarkPlugins: [remarkMkDocsAdmonition, remarkPyMdownTabs, [removeTitle, meta.title]],
rehypePlugins: [[linkTransform, urlTransform], rehypeSlug],
},
} as SerializeOptions
return <GuideTemplate mdxOptions={options} meta={meta} {...data} />
}
/**
* Fetch markdown from external repo
*/
const getContent = async ({ slug }: Params) => {
const page = pageMap.find(({ slug: validSlug }) => validSlug && validSlug === slug)
if (!page) {
redirect(notFoundLink(`cli/github-action/${slug}`))
}
const { remoteFile, meta } = page
const editLink = newEditLink(`${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}`)
const response = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/${remoteFile}`
)
const content = await response.text()
return {
pathname: `/guides/cli/github-action/${slug}` satisfies `/${string}`,
meta,
content,
editLink,
}
}
const urlTransform: UrlTransformFunction = (url) => {
try {
const externalSiteUrl = new URL(externalSite)
const placeholderHostname = 'placeholder'
const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`)
// Don't modify a url with a FQDN or a url that's only a hash
if (hostname !== placeholderHostname || pathname === '/') {
return url
}
const relativePage = (
pathname.endsWith('.md')
? pathname.replace(/\.md$/, '')
: relative(externalSiteUrl.pathname, pathname)
).replace(/^\//, '')
const page = pageMap.find(({ remoteFile }) => `${relativePage}.md` === remoteFile)
// If we have a mapping for this page, use the mapped path
if (page) {
return page.slug + hash
}
// If we don't have this page in our docs, link to original docs
return `${externalSite}/${relativePage}${hash}`
} catch (err) {
console.error('Error transforming markdown URL', err)
return url
}
}
const generateStaticParams = () => pageMap.map(({ slug }) => ({ slug }))
const generateMetadata = genGuideMeta(getContent)
export default ActionDocs
export { generateStaticParams, generateMetadata }
@@ -0,0 +1,161 @@
import { Octokit } from '@octokit/core'
import { capitalize } from 'lodash'
import { type SerializeOptions } from 'next-mdx-remote/dist/types'
import rehypeSlug from 'rehype-slug'
import { Heading } from 'ui'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { Tabs, TabPanel } from '~/features/ui/Tabs'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
// We fetch these docs at build time from an external repo
const org = 'supabase'
const repo = 'splinter'
const branch = 'main'
const docsDir = 'docs'
const meta = {
title: 'Performance and Security Advisors',
subtitle: 'Check your database for performance and security issues',
}
const generateMetadata = genGuideMeta(() => ({
pathname: '/guides/database/database-linter',
meta,
}))
const editLink = newEditLink('supabase/splinter/tree/main/docs')
const markdownIntro = `
You can use the Database Performance and Security Advisors to check your database for issues such as missing indexes and improperly set-up RLS policies.
## Using the Advisors
In the dashboard, navigate to [Security Advisor](https://supabase.com/dashboard/project/_/database/security-advisor) and [Performance Advisor](https://supabase.com/dashboard/project/_/database/performance-advisor) under Database. The advisors run automatically. You can also manually rerun them after you've resolved issues.
`.trim()
const getBasename = (path: string) => path.split('/').at(-1)!.replace(/\.md$/, '')
const DatabaseAdvisorDocs = async () => {
const { lints, lintsList } = await getLints()
const options = {
mdxOptions: {
remarkPlugins: [remarkMkDocsAdmonition, remarkPyMdownTabs, [removeTitle, meta.title]],
rehypePlugins: [[linkTransform, urlTransform(lintsList)], rehypeSlug],
},
} as SerializeOptions
return (
<GuideTemplate meta={meta} editLink={editLink}>
<MDXRemoteGuides source={markdownIntro} />
<Heading tag="h2">Available checks</Heading>
<Tabs listClassNames="flex flex-wrap gap-2 [&>button]:!m-0" queryGroup="lint">
{lints.map((lint) => (
<TabPanel
key={lint.path}
id={lint.path}
label={capitalize(getBasename(lint.path).replace(/_/g, ' '))}
>
<section id={getBasename(lint.path)}>
<MDXRemoteGuides source={lint.content} options={options} />
</section>
</TabPanel>
))}
</Tabs>
</GuideTemplate>
)
}
/**
* The GitHub repo uses relative links, which don't lead to the right locations
* in docs.
*
* @param url The original link, as written in the Markdown file
* @returns The rewritten link
*/
const urlTransform: (lints: Array<{ path: string }>) => UrlTransformFunction = (lints) => (url) => {
try {
const placeholderHostname = 'placeholder'
const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`)
// Don't modify a url with a FQDN or a url that's only a hash
if (hostname !== placeholderHostname || pathname === '/') {
return url
}
const relativePath = getBasename(pathname)
const section = lints.find(({ path }) => path === relativePath)
if (section) {
const url = new URL(window.location.href)
url.searchParams.set('lint', relativePath)
return url.toString()
}
// If we don't have this page in our docs, link to GitHub repo
return `https://github.com/${org}/${repo}/blob/${branch}${pathname}${hash}`
} catch (err) {
console.error('Error transforming markdown URL', err)
return url
}
}
/**
* Fetch lint remediation Markdown from external repo
*/
const getLints = async () => {
const octokit = new Octokit({ request: { fetch: fetchRevalidatePerDay } })
const response = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', {
owner: org,
repo: repo,
path: docsDir,
ref: branch,
headers: {
'X-GitHub-Api-Version': '2022-11-28',
},
})
if (response.status >= 400) {
throw Error(`Could not get contents of repo ${org}/${repo}`)
}
if (!Array.isArray(response.data)) {
throw Error(
'Reading a directory, not a file. Should not reach this, solely to appease Typescript.'
)
}
const lintsList = response.data.filter(({ path }) => /docs\/\d+.+\.md$/.test(path))
const lints = await Promise.all(
lintsList.map(async ({ path }) => {
const fileResponse = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${org}/${repo}/${branch}/${path}`
)
if (fileResponse.status >= 400) {
throw Error(`Could not get contents of file ${org}/${repo}/${path}`)
}
const content = await fileResponse.text()
return {
path: getBasename(path),
content,
}
})
)
return { lints, lintsList }
}
export default DatabaseAdvisorDocs
export { generateMetadata }
@@ -0,0 +1,222 @@
import matter from 'gray-matter'
import { type SerializeOptions } from 'next-mdx-remote/dist/types'
import { readFile } from 'node:fs/promises'
import { join, relative } from 'node:path'
import rehypeSlug from 'rehype-slug'
import emoji from 'remark-emoji'
import { genGuideMeta, genGuidesStaticParams } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { GUIDES_DIRECTORY, isValidGuideFrontmatter } from '~/lib/docs'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
// We fetch these docs at build time from an external repo
const org = 'supabase'
const repo = 'wrappers'
const branch = 'main'
const docsDir = 'docs'
const externalSite = 'https://supabase.github.io/wrappers'
// Each external docs page is mapped to a local page
const pageMap = [
{
slug: 'airtable',
meta: {
title: 'Airtable',
},
remoteFile: 'airtable.md',
},
{
slug: 'auth0',
meta: {
title: 'Auth0',
},
remoteFile: 'auth0.md',
},
{
slug: 'bigquery',
meta: {
title: 'BigQuery',
},
remoteFile: 'bigquery.md',
},
{
slug: 'clickhouse',
meta: {
title: 'ClickHouse',
},
remoteFile: 'clickhouse.md',
},
{
slug: 'cognito',
meta: {
title: 'AWS Cognito',
},
remoteFile: 'cognito.md',
},
{
slug: 'firebase',
meta: {
title: 'Firebase',
},
remoteFile: 'firebase.md',
},
{
slug: 'logflare',
meta: {
title: 'Logflare',
},
remoteFile: 'logflare.md',
},
{
slug: 'mssql',
meta: {
title: 'MSSQL',
},
remoteFile: 'mssql.md',
},
{
slug: 'redis',
meta: {
title: 'Redis',
},
remoteFile: 'redis.md',
},
{
slug: 's3',
meta: {
title: 'AWS S3',
},
remoteFile: 's3.md',
},
{
slug: 'stripe',
meta: {
title: 'Stripe',
},
remoteFile: 'stripe.md',
},
]
interface Params {
slug?: string[]
}
const WrappersDocs = async ({ params }: { params: Params }) => {
const { isExternal, meta, ...data } = await getContent(params)
const options = isExternal
? ({
mdxOptions: {
remarkPlugins: [
remarkMkDocsAdmonition,
emoji,
remarkPyMdownTabs,
[removeTitle, meta.title],
],
rehypePlugins: [[linkTransform, urlTransform], rehypeSlug],
},
} as SerializeOptions)
: undefined
return <GuideTemplate meta={meta} mdxOptions={options} {...data} />
}
/**
* Fetch markdown from external repo
*/
const getContent = async (params: Params) => {
const federatedPage = pageMap.find(
({ slug }) => params && slug && params.slug && slug === params.slug.at(0)
)
let isExternal: boolean
let meta: any
let content: string
let editLink: string
if (!federatedPage) {
isExternal = false
editLink = `supabase/supabase/apps/docs/content/guides/database/extensions/wrappers${params.slug?.length ? `/${params.slug.join('/')}` : ''}.mdx`
const rawContent = await readFile(
join(
GUIDES_DIRECTORY,
'database',
'extensions',
`wrappers${params.slug?.length ? `/${params.slug.join('/')}` : ''}.mdx`
),
'utf-8'
)
;({ data: meta, content } = matter(rawContent))
if (!isValidGuideFrontmatter(meta)) {
throw Error(`Expected valid frontmatter, got ${JSON.stringify(meta, null, 2)}`)
}
} else {
isExternal = true
let remoteFile: string
;({ remoteFile, meta } = federatedPage)
const repoPath = `${org}/${repo}/${branch}/${docsDir}/${remoteFile}`
editLink = `${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}`
const response = await fetchRevalidatePerDay(`https://raw.githubusercontent.com/${repoPath}`)
content = await response.text()
}
return {
pathname:
`/guides/database/extensions/wrappers${params.slug?.length ? `/${params.slug.join('/')}` : ''}` satisfies `/${string}`,
isExternal,
editLink: newEditLink(editLink),
meta,
content,
}
}
const urlTransform: UrlTransformFunction = (url) => {
try {
const externalSiteUrl = new URL(externalSite)
const placeholderHostname = 'placeholder'
const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`)
// Don't modify a url with a FQDN or a url that's only a hash
if (hostname !== placeholderHostname || pathname === '/') {
return url
}
const relativePage = (
pathname.endsWith('.md')
? pathname.replace(/\.md$/, '')
: relative(externalSiteUrl.pathname, pathname)
).replace(/^\//, '')
const page = pageMap.find(({ remoteFile }) => `${relativePage}.md` === remoteFile)
// If we have a mapping for this page, use the mapped path
if (page) {
return page.slug + hash
}
// If we don't have this page in our docs, link to original docs
return `${externalSite}/${relativePage}${hash}`
} catch (err) {
console.error('Error transforming markdown URL', err)
return url
}
}
const generateStaticParams = async () => {
const mdxPaths = await genGuidesStaticParams('database/extensions/wrappers')()
const federatedPaths = pageMap.map(({ slug }) => ({
slug: [slug],
}))
return [...mdxPaths, ...federatedPaths]
}
const generateMetadata = genGuideMeta(getContent)
export default WrappersDocs
export { generateStaticParams, generateMetadata }
@@ -0,0 +1,194 @@
import { type SerializeOptions } from 'next-mdx-remote/dist/types'
import { redirect } from 'next/navigation'
import { isAbsolute, relative } from 'path'
import rehypeSlug from 'rehype-slug'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { notFoundLink } from '~/features/recommendations/NotFound.utils'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
// We fetch these docs at build time from an external repo
const org = 'supabase'
const repo = 'pg_graphql'
const branch = 'master'
const docsDir = 'docs'
const externalSite = 'https://supabase.github.io/pg_graphql'
// Each external docs page is mapped to a local page
const pageMap = [
{
meta: {
id: 'graphql-overview',
title: 'GraphQL',
subtitle: 'Autogenerated GraphQL APIs with Postgres.',
},
remoteFile: 'supabase.md',
},
{
slug: 'api',
meta: {
id: 'graphql-api',
title: 'GraphQL API',
subtitle: 'Understanding the core concepts of the GraphQL API.',
},
remoteFile: 'api.md',
},
{
slug: 'views',
meta: {
id: 'graphql-views',
title: 'Views',
subtitle: 'Using Postgres Views with GraphQL.',
},
remoteFile: 'views.md',
},
{
slug: 'functions',
meta: {
id: 'graphql-functions',
title: 'Functions',
subtitle: 'Using Postgres Functions with GraphQL.',
},
remoteFile: 'functions.md',
},
{
slug: 'computed-fields',
meta: {
id: 'graphql-computed-fields',
title: 'Computed Fields',
subtitle: 'Using Postgres Computed Fields with GraphQL.',
},
remoteFile: 'computed-fields.md',
},
{
slug: 'configuration',
meta: {
id: 'graphql-configuration',
title: 'Configuration & Customization',
subtitle: 'Extra configuration options can be set on SQL entities using comment directives.',
},
remoteFile: 'configuration.md',
},
{
slug: 'security',
meta: {
id: 'graphql-security',
title: 'Security',
subtitle: 'Securing your GraphQL API.',
},
remoteFile: 'security.md',
},
{
slug: 'with-apollo',
meta: {
id: 'graphql-with-apollo',
title: 'With Apollo',
subtitle: 'Using pg_grapqhl with Apollo.',
},
remoteFile: 'usage_with_apollo.md',
},
{
slug: 'with-relay',
meta: {
id: 'graphql-with-relay',
title: 'With Relay',
subtitle: 'Using pg_grapqhl with Relay.',
},
remoteFile: 'usage_with_relay.md',
},
]
interface Params {
slug?: string[]
}
const PGGraphQLDocs = async ({ params }: { params: Params }) => {
const { meta, ...data } = await getContent(params)
const options = {
mdxOptions: {
remarkPlugins: [remarkMkDocsAdmonition, remarkPyMdownTabs, [removeTitle, meta.title]],
rehypePlugins: [[linkTransform, urlTransform], rehypeSlug],
},
} as SerializeOptions
return <GuideTemplate mdxOptions={options} meta={meta} {...data} />
}
/**
* Fetch markdown from external repo and transform links
*/
const getContent = async ({ slug }: Params) => {
const page = pageMap.find((page) => page.slug === slug?.at(0))
if (!page) {
redirect(notFoundLink(`graphql/${slug ? slug.join('/') : ''}`))
}
const { remoteFile, meta } = page
const editLink = newEditLink(`${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}`)
const response = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${org}/${repo}/${branch}/${docsDir}/${remoteFile}`
)
const content = await response.text()
return {
pathname: `/guides/graphql${slug?.length ? `/${slug.join('/')}` : ''}` satisfies `/${string}`,
meta,
content,
editLink,
}
}
const urlTransform: UrlTransformFunction = (url) => {
try {
const externalSiteUrl = new URL(externalSite)
const placeholderHostname = 'placeholder'
const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`)
// Don't modify a url with a FQDN or a url that's only a hash
if (hostname !== placeholderHostname || pathname === '/') {
return url
}
const getRelativePath = () => {
if (pathname.endsWith('.md')) {
return pathname.replace(/\.md$/, '')
}
if (isAbsolute(url)) {
return relative(externalSiteUrl.pathname, pathname)
}
return pathname
}
const relativePath = getRelativePath().replace(/^\//, '')
const page = pageMap.find(({ remoteFile }) => `${relativePath}.md` === remoteFile)
// If we have a mapping for this page, use the mapped path
if (page) {
return 'graphql/' + page.slug + hash
}
// If we don't have this page in our docs, link to original docs
return `${externalSite}/${relativePath}${hash}`
} catch (err) {
console.error('Error transforming markdown URL', err)
return url
}
}
const generateStaticParams = async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] }))
const generateMetadata = genGuideMeta(getContent)
export default PGGraphQLDocs
export { generateStaticParams, generateMetadata }
+50
View File
@@ -0,0 +1,50 @@
'use client'
import { usePathname } from 'next/navigation'
import { type PropsWithChildren } from 'react'
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
import Layout from '~/layouts/guides'
const GuidesLayout = ({ children }: PropsWithChildren) => {
const pathname = usePathname()
const menuId = getMenuId(pathname)
return <Layout menuId={menuId}>{children}</Layout>
}
export const getMenuId = (pathname: string | null) => {
pathname = (pathname ??= '').replace(/^\/guides\//, '')
switch (true) {
case pathname.startsWith('ai'):
return MenuId.Ai
case pathname.startsWith('api'):
return MenuId.Api
case pathname.startsWith('auth'):
return MenuId.Auth
case pathname.startsWith('cli'):
return MenuId.Cli
case pathname.startsWith('database'):
return MenuId.Database
case pathname.startsWith('functions'):
return MenuId.Functions
case pathname.startsWith('getting-started'):
return MenuId.GettingStarted
case pathname.startsWith('graphql'):
return MenuId.Graphql
case pathname.startsWith('platform'):
return MenuId.Platform
case pathname.startsWith('realtime'):
return MenuId.Realtime
case pathname.startsWith('resources'):
return MenuId.Resources
case pathname.startsWith('self-hosting'):
return MenuId.SelfHosting
case pathname.startsWith('storage'):
return MenuId.Storage
default:
return MenuId.GettingStarted
}
}
export default GuidesLayout
@@ -0,0 +1,142 @@
import matter from 'gray-matter'
import { type SerializeOptions } from 'next-mdx-remote/dist/types'
import { redirect } from 'next/navigation'
import rehypeSlug from 'rehype-slug'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { notFoundLink } from '~/features/recommendations/NotFound.utils'
import { isValidGuideFrontmatter } from '~/lib/docs'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
import {
terraformDocsBranch,
terraformDocsDocsDir,
terraformDocsOrg,
terraformDocsRepo,
} from '../terraformConstants'
// Each external docs page is mapped to a local page
const pageMap = [
{
remoteFile: 'README.md',
meta: {
title: 'Terraform Provider',
},
useRoot: true,
},
{
slug: 'tutorial',
remoteFile: 'tutorial.md',
meta: {
title: 'Using the Supabase Terraform Provider',
},
},
]
interface Params {
slug?: string[]
}
const TerraformDocs = async ({ params }: { params: Params }) => {
const { meta, ...data } = await getContent(params)
const options = {
mdxOptions: {
remarkPlugins: [remarkMkDocsAdmonition, remarkPyMdownTabs, [removeTitle, meta.title]],
rehypePlugins: [[linkTransform, urlTransform], rehypeSlug],
},
} as SerializeOptions
return <GuideTemplate mdxOptions={options} meta={meta} {...data} />
}
/**
* The GitHub repo uses relative links, which don't lead to the right locations
* in docs.
*
* @param url The original link, as written in the Markdown file
* @returns The rewritten link
*/
const urlTransform: UrlTransformFunction = (url: string) => {
try {
const placeholderHostname = 'placeholder'
const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`)
// Don't modify a url with a FQDN or a url that's only a hash
if (hostname !== placeholderHostname || pathname === '/') {
return url
}
const getBasename = (pathname: string) =>
pathname.endsWith('.md') ? pathname.replace(/\.md$/, '') : pathname
const stripLeadingPrefix = (pathname: string) => pathname.replace(/^\//, '')
const stripLeadingDocs = (pathname: string) => pathname.replace(/^docs\//, '')
const relativePath = stripLeadingPrefix(getBasename(pathname))
const page = pageMap.find(
({ remoteFile, useRoot }) =>
(useRoot && `${relativePath}.md` === remoteFile) ||
(!useRoot && `${stripLeadingDocs(relativePath)}.md` === remoteFile)
)
if (page) {
return 'terraform' + `/${page.slug}` + hash
}
// If we don't have this page in our docs, link to GitHub repo
return `https://github.com/${terraformDocsOrg}/${terraformDocsRepo}/blob/${terraformDocsBranch}${pathname}${hash}`
} catch (err) {
console.error('Error transforming markdown URL', err)
return url
}
}
/**
* Fetch markdown from external repo
*/
const getContent = async ({ slug }: Params) => {
const [requestedSlug] = slug ?? []
const page = pageMap.find((page) => page.slug === requestedSlug)
if (!page) {
redirect(notFoundLink('platform/terraform' + (slug?.join('/') ?? '')))
}
const { meta, remoteFile, useRoot } = page
const editLink = newEditLink(
`${terraformDocsOrg}/${terraformDocsRepo}/blob/${terraformDocsBranch}/${useRoot ? '' : `${terraformDocsDocsDir}/`}${remoteFile}`
)
let response = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${terraformDocsOrg}/${terraformDocsRepo}/${terraformDocsBranch}/${useRoot ? '' : `${terraformDocsDocsDir}/`}${remoteFile}`
)
let rawContent = await response.text()
// Strip out HTML comments
rawContent = rawContent.replace(/<!--.*?-->/, '')
const { content, data } = matter(rawContent)
Object.assign(meta, data)
if (!isValidGuideFrontmatter(meta)) {
throw Error('Guide frontmatter is invalid.')
}
return {
pathname:
`/guides/platform/terraform${slug?.length ? `/${slug.join('/')}` : ''}` satisfies `/${string}`,
meta,
content,
editLink,
}
}
const generateStaticParams = async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] }))
const generateMetadata = genGuideMeta(getContent)
export default TerraformDocs
export { generateStaticParams, generateMetadata }
@@ -0,0 +1,410 @@
import { codeBlock } from 'common-tags'
import { Check, PlusCircle } from 'lucide-react'
import Link from 'next/link'
import ReactMarkdown from 'react-markdown'
import {
CodeBlock,
Heading,
PopoverContent_Shadcn_,
PopoverTrigger_Shadcn_,
Popover_Shadcn_,
} from 'ui'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { TabPanel, Tabs } from '~/features/ui/Tabs'
import {
terraformDocsBranch,
terraformDocsDocsDir,
terraformDocsOrg,
terraformDocsRepo,
} from '../terraformConstants'
const meta = {
title: 'Terraform Provider reference',
subtitle: 'Resources and data sources available through the Terraform Provider',
}
const generateMetadata = genGuideMeta(() => ({
pathname: '/guides/platform/terraform/reference',
meta,
}))
function ProviderSettings({ schema }: { schema: any }) {
const attributes = schema.block.attributes
const example = codeBlock`
provider "supabase" {
${Object.keys(attributes).map(
(attribute) =>
`${attribute} = ${attributes[attribute].type === 'string' ? `""` : '<value>'}`
)}
}
`
return (
<section aria-labelledby="provider-settings" className="prose max-w-none">
<Heading tag="h2">Provider settings</Heading>
<p>
Use these settings to configure your Supabase provider and authenticate to your Supabase
project.
</p>
<Heading tag="h3">Example usage</Heading>
<CodeBlock className="not-prose">{example}</CodeBlock>
<Heading tag="h3">Details</Heading>
{/* extra div because width restriction doesn't work on table itself */}
<div className="w-full overflow-auto">
<table>
<thead>
<tr>
<th>Attribute</th>
<th>Description</th>
<th>Type</th>
<th>Optional</th>
<th>Sensitive</th>
</tr>
</thead>
<tbody>
{Object.keys(schema.block.attributes).map((attribute) => (
<tr key={attribute}>
<td>{attribute}</td>
<td>
<ReactMarkdown>{attributes[attribute].description}</ReactMarkdown>
</td>
<td>{attributes[attribute].type}</td>
<td className="align-middle">
{attributes[attribute].optional && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
<td className="align-middle">
{attributes[attribute].sensitive && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
)
}
function Resources({ schema }: { schema: any }) {
return (
<section aria-labelledby="resources" className="prose max-w-none">
<Heading tag="h2">Resources</Heading>
<p>You can configure these resources using the Supabase Terraform provider:</p>
<Tabs>
{Object.keys(schema).map((resource) => (
<TabPanel id={resource} label={resource}>
<Heading tag="h4">Example usage</Heading>
<CodeBlock className="not-prose">{codeBlock`
resource "${resource}" "<label>" {
${Object.keys(schema[resource].block.attributes)
.filter((attribute) => !schema[resource].block.attributes[attribute].computed)
.map(
(attribute) =>
`${attribute} = ${
schema[resource].block.attributes[attribute].type === 'string'
? `""`
: '<value>'
}`
)}
}
`}</CodeBlock>
<Heading tag="h4">Details</Heading>
<table>
<thead>
<tr>
<th>Attribute</th>
<th>Description</th>
<th>Type</th>
<th>Required</th>
<th>Optional</th>
<th>Read-only</th>
</tr>
</thead>
<tbody>
{Object.keys(schema[resource].block.attributes).map((attribute) => (
<tr key={attribute}>
<td>
<code>{attribute}</code>
</td>
<td>
<ReactMarkdown>
{schema[resource].block.attributes[attribute].description}
</ReactMarkdown>
</td>
<td>
{schema[resource].block.attributes[attribute].type ?? (
<Popover_Shadcn_>
<PopoverTrigger_Shadcn_ asChild>
<button className="flex items-center justify-between gap-2">
Nested type
<PlusCircle size={14} className="shrink-0" />
</button>
</PopoverTrigger_Shadcn_>
<PopoverContent_Shadcn_ className="max-h-[50vh] overflow-auto">
<ul>
{Object.keys(
schema[resource].block.attributes[attribute].nested_type.attributes
).map((nestedAttribute) => (
<li key={nestedAttribute}>
{nestedAttribute}
<ul>
<li>
<ReactMarkdown className="*:!m-0">
{
schema[resource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].description
}
</ReactMarkdown>
</li>
<li>
{schema[resource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].type ?? 'nested type'}
</li>
{schema[resource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].required && <li>Required</li>}
{schema[resource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].optional && <li>Optional</li>}
{schema[resource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].computed && <li>Read-only</li>}
</ul>
</li>
))}
</ul>
</PopoverContent_Shadcn_>
</Popover_Shadcn_>
)}
</td>
<td className="align-middle">
{schema[resource].block.attributes[attribute].required && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
<td className="align-middle">
{schema[resource].block.attributes[attribute].optional && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
<td className="align-middle">
{schema[resource].block.attributes[attribute].computed && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
</tr>
))}
</tbody>
</table>
</TabPanel>
))}
</Tabs>
</section>
)
}
function DataSources({ schema }: { schema: any }) {
return (
<section aria-labelledby="data-sources" className="prose max-w-none">
<Heading tag="h2">Data sources</Heading>
<p>You can read these resources using the Supabase Terraform provider:</p>
<Tabs>
{Object.keys(schema).map((dataSource) => (
<TabPanel id={dataSource} label={dataSource}>
<Heading tag="h4">Example usage</Heading>
<CodeBlock className="not-prose">{codeBlock`
resource "${dataSource}" "all" {
${Object.keys(schema[dataSource].block.attributes)
.filter(
(attribute) => !schema[dataSource].block.attributes[attribute].computed
)
.map(
(attribute) =>
`${attribute} = ${
schema[dataSource].block.attributes[attribute].type === 'string'
? `""`
: '<value>'
}`
)}
}
`}</CodeBlock>
<Heading tag="h4">Details</Heading>
<table>
<thead>
<tr>
<th>Attribute</th>
<th>Description</th>
<th>Type</th>
<th>Required</th>
<th>Optional</th>
<th>Read-only</th>
</tr>
</thead>
<tbody>
{Object.keys(schema[dataSource].block.attributes).map((attribute) => (
<tr key={attribute}>
<td>
<code>{attribute}</code>
</td>
<td>
<ReactMarkdown>
{schema[dataSource].block.attributes[attribute].description}
</ReactMarkdown>
</td>
<td>
{schema[dataSource].block.attributes[attribute].type ?? (
<Popover_Shadcn_>
<PopoverTrigger_Shadcn_ asChild>
<button className="flex items-center justify-between gap-2">
Nested type
<PlusCircle size={14} />
</button>
</PopoverTrigger_Shadcn_>
<PopoverContent_Shadcn_ className="max-h-[50vh] overflow-auto">
{schema[dataSource].block.attributes[attribute].nested_type
.nesting_mode === 'set' && 'Array of:'}
<ul>
{Object.keys(
schema[dataSource].block.attributes[attribute].nested_type
.attributes
).map((nestedAttribute) => (
<li key={nestedAttribute}>
{nestedAttribute}
<ul>
<li>
<ReactMarkdown className="*:!m-0">
{
schema[dataSource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].description
}
</ReactMarkdown>
</li>
<li>
{schema[dataSource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].type ?? 'nested type'}
</li>
{schema[dataSource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].required && <li>Required</li>}
{schema[dataSource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].optional && <li>Optional</li>}
{schema[dataSource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].computed && <li>Read-only</li>}
</ul>
</li>
))}
</ul>
</PopoverContent_Shadcn_>
</Popover_Shadcn_>
)}
</td>
<td className="align-middle">
{schema[dataSource].block.attributes[attribute].required && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
<td className="align-middle">
{schema[dataSource].block.attributes[attribute].optional && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
<td className="align-middle">
{schema[dataSource].block.attributes[attribute].computed && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
</tr>
))}
</tbody>
</table>
</TabPanel>
))}
</Tabs>
</section>
)
}
const TerraformReferencePage = async () => {
const { schema } = await getSchema()
const editLink = newEditLink('supabase/terraform-provider-supabase')
return (
<GuideTemplate meta={meta} editLink={editLink}>
The Terraform Provider provices access to{' '}
<Link
href="https://developer.hashicorp.com/terraform/language/resources"
rel="noopener noreferrer"
>
resources
</Link>{' '}
and{' '}
<Link
href="https://developer.hashicorp.com/terraform/language/data-sources"
rel="noreferrer noopener"
>
data sources
</Link>
. Resources are infrastructure objects, such as a Supabase project, that you can declaratively
configure. Data sources are sources of information about your Supabase instances.
<ProviderSettings
schema={schema.provider_schemas['registry.terraform.io/supabase/supabase'].provider}
/>
<Resources
schema={schema.provider_schemas['registry.terraform.io/supabase/supabase'].resource_schemas}
/>
<DataSources
schema={
schema.provider_schemas['registry.terraform.io/supabase/supabase'].data_source_schemas
}
/>
</GuideTemplate>
)
}
/**
* Fetch JSON schema from external repo
*/
const getSchema = async () => {
let response = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${terraformDocsOrg}/${terraformDocsRepo}/${terraformDocsBranch}/${terraformDocsDocsDir}/schema.json`
)
if (!response.ok) throw Error('Failed to fetch Terraform JSON schema from GitHub')
const schema = await response.json()
return {
schema,
}
}
export default TerraformReferencePage
export { generateMetadata }
@@ -0,0 +1,9 @@
/**
* Information on where to fetch docs content
*/
const terraformDocsOrg = 'supabase'
const terraformDocsRepo = 'terraform-provider-supabase'
const terraformDocsBranch = 'v1.1.3'
const terraformDocsDocsDir = 'docs'
export { terraformDocsOrg, terraformDocsRepo, terraformDocsBranch, terraformDocsDocsDir }
@@ -0,0 +1,64 @@
import Param from '~/components/Params'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template'
import specAnalyticsV0 from '~/spec/analytics_v0_config.yaml' assert { type: 'yml' }
const meta = {
title: 'Analytics Self-hosting Config',
description: 'How to configure and deploy Supabase Analytics.',
}
const generateMetadata = genGuideMeta(() => ({
pathname: '/guides/self-hosting/analytics/config',
meta,
}))
const AnalyticsConfigPage = async () => {
const descriptionMdx = specAnalyticsV0.info.description
return (
<GuideTemplate
meta={meta}
editLink={newEditLink(
'supabase/supabase/blob/master/apps/docs/pages/guides/self-hosting/analytics/config.tsx'
)}
>
<MDXRemoteGuides source={descriptionMdx} />
<div>
{specAnalyticsV0.info.tags.map(
(tag: ReturnType<typeof specAnalyticsV0>['info']['tags']) => {
return (
<>
<h2 className="text-foreground">{tag.title}</h2>
<p className="text-foreground-lighter">{tag.description}</p>
<div className="not-prose">
<h5 className="text-base text-foreground mb-3">Parameters</h5>
<ul>
{specAnalyticsV0.parameters
.filter((param: ReturnType<typeof specAnalyticsV0>['parameters']) =>
param.tags.includes(tag.id)
)
.map((param: ReturnType<typeof specAnalyticsV0>['parameters']) => {
return (
<Param
name={param.title}
type={param.type}
description={param.description}
required={param.required}
/>
)
})}
</ul>
</div>
</>
)
}
)}
</div>
</GuideTemplate>
)
}
export default AnalyticsConfigPage
export { generateMetadata }
@@ -0,0 +1,62 @@
import Param from '~/components/Params'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template'
import specAuthV1 from '~/spec/gotrue_v1_config.yaml' assert { type: 'yml' }
const meta = {
title: 'Auth Self-hosting Config',
description: 'How to configure and deploy Supabase Auth.',
}
const generateMetadata = genGuideMeta(() => ({
pathname: '/guides/self-hosting/auth/config',
meta,
}))
const AuthConfigPage = async () => {
const descriptionMdx = specAuthV1.info.description
return (
<GuideTemplate
meta={meta}
editLink={newEditLink(
'supabase/supabase/blob/master/apps/docs/pages/guides/self-hosting/auth/config.tsx'
)}
>
<MDXRemoteGuides source={descriptionMdx} />
<div>
{specAuthV1.info.tags.map((tag: ReturnType<typeof specAuthV1>['info']['tags']) => {
return (
<>
<h2 className="text-foreground">{tag.title}</h2>
<p className="text-foreground-lighter">{tag.description}</p>
<div className="not-prose">
<h5 className="text-base text-foreground mb-3">Parameters</h5>
<ul>
{specAuthV1.parameters
.filter((param: ReturnType<typeof specAuthV1>['parameters']) =>
param.tags.includes(tag.id)
)
.map((param: ReturnType<typeof specAuthV1>['parameters']) => {
return (
<Param
name={param.title}
type={param.type}
description={param.description}
required={param.required}
/>
)
})}
</ul>
</div>
</>
)
})}
</div>
</GuideTemplate>
)
}
export default AuthConfigPage
export { generateMetadata }
@@ -0,0 +1,62 @@
import Param from '~/components/Params'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template'
import specRealtimeV0 from '~/spec/realtime_v0_config.yaml' assert { type: 'yml' }
const meta = {
title: 'Realtime Self-hosting Config',
description: 'How to configure and deploy Supabase Realtime.',
}
const generateMetadata = genGuideMeta(() => ({
pathname: '/guides/self-hosting/realtime/config',
meta,
}))
const RealtimeConfigPage = async () => {
const descriptionMdx = specRealtimeV0.info.description
return (
<GuideTemplate
meta={meta}
editLink={newEditLink(
'supabase/supabase/blob/master/apps/docs/pages/guides/self-hosting/realtime/config.tsx'
)}
>
<MDXRemoteGuides source={descriptionMdx} />
<div>
{specRealtimeV0.info.tags.map((tag: ReturnType<typeof specRealtimeV0>['info']['tags']) => {
return (
<>
<h2 className="text-foreground">{tag.title}</h2>
<p className="text-foreground-lighter">{tag.description}</p>
<div className="not-prose">
<h5 className="text-base text-foreground mb-3">Parameters</h5>
<ul>
{specRealtimeV0.parameters
.filter((param: ReturnType<typeof specRealtimeV0>['parameters']) =>
param.tags.includes(tag.id)
)
.map((param: ReturnType<typeof specRealtimeV0>['parameters']) => {
return (
<Param
name={param.title}
type={param.type}
description={param.description}
required={param.required}
/>
)
})}
</ul>
</div>
</>
)
})}
</div>
</GuideTemplate>
)
}
export default RealtimeConfigPage
export { generateMetadata }
@@ -0,0 +1,62 @@
import Param from '~/components/Params'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, MDXRemoteGuides, newEditLink } from '~/features/docs/GuidesMdx.template'
import specStorageV0 from '~/spec/storage_v0_config.yaml' assert { type: 'yml' }
const meta = {
title: 'Storage Self-hosting Config',
description: 'How to configure and deploy Supabase Storage.',
}
const generateMetadata = genGuideMeta(() => ({
pathname: '/guides/self-hosting/storage/config',
meta,
}))
const StorageConfigPage = async () => {
const descriptionMdx = specStorageV0.info.description
return (
<GuideTemplate
meta={meta}
editLink={newEditLink(
'supabase/supabase/blob/master/apps/docs/pages/guides/self-hosting/storage/config.tsx'
)}
>
<MDXRemoteGuides source={descriptionMdx} />
<div>
{specStorageV0.info.tags.map((tag: ReturnType<typeof specStorageV0>['info']['tags']) => {
return (
<>
<h2 className="text-foreground">{tag.title}</h2>
<p className="text-foreground-lighter">{tag.description}</p>
<div className="not-prose">
<h5 className="text-base text-foreground mb-3">Parameters</h5>
<ul>
{specStorageV0.parameters
.filter((param: ReturnType<typeof specStorageV0>['parameters']) =>
param.tags.includes(tag.id)
)
.map((param: ReturnType<typeof specStorageV0>['parameters']) => {
return (
<Param
name={param.title}
type={param.type}
description={param.description}
required={param.required}
/>
)
})}
</ul>
</div>
</>
)
})}
</div>
</GuideTemplate>
)
}
export default StorageConfigPage
export { generateMetadata }
+17 -17
View File
@@ -22,8 +22,8 @@
"dependencies": {
"@code-hike/mdx": "^0.9.0",
"@har-sdk/openapi-sampler": "^2.2.0",
"@mdx-js/loader": "^2.1.5",
"@mdx-js/react": "^2.3.0",
"@mdx-js/loader": "^3.0.1",
"@mdx-js/react": "^3.0.1",
"@next/bundle-analyzer": "^14.2.3",
"@next/mdx": "^14.2.3",
"@octokit/auth-app": "^6.0.3",
@@ -42,7 +42,7 @@
"framer-motion": "^11.0.3",
"github-slugger": "^2.0.0",
"gray-matter": "^4.0.3",
"hast-util-has-property": "^2.0.1",
"hast-util-has-property": "^3.0.0",
"icons": "*",
"isbot": "^5.1.2",
"jsrsasign": "^11.0.0",
@@ -50,12 +50,12 @@
"lodash": "^4.17.21",
"mdast": "^3.0.0",
"mdast-util-from-markdown": "^1.2.0",
"mdast-util-mdx": "^2.0.0",
"mdast-util-to-markdown": "^1.5.0",
"mdast-util-to-string": "^3.1.1",
"micromark-extension-mdxjs": "^1.0.0",
"mdast-util-mdx": "^3.0.0",
"mdast-util-to-markdown": "^2.1.0",
"mdast-util-to-string": "^4.0.0",
"micromark-extension-mdxjs": "^3.0.0",
"next": "^14.2.3",
"next-mdx-remote": "^4.4.1",
"next-mdx-remote": "^5.0.0",
"next-plugin-yaml": "^1.0.1",
"next-seo": "^6.5.0",
"next-themes": "^0.3.0",
@@ -67,27 +67,27 @@
"react-intersection-observer": "^9.5.3",
"react-markdown": "^8.0.3",
"rehype-katex": "^7.0.0",
"rehype-slug": "^5.1.0",
"remark": "^14.0.2",
"remark-emoji": "^3.1.2",
"remark-gfm": "^3.0.1",
"rehype-slug": "^6.0.0",
"remark": "^15.0.1",
"remark-emoji": "^5.0.1",
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"shared-data": "*",
"ui": "*",
"ui-patterns": "*",
"unist-builder": "^3.0.1",
"unist-util-filter": "^4.0.1",
"unist-util-visit": "^4.1.2",
"unist-builder": "^4.0.0",
"unist-util-filter": "^5.0.1",
"unist-util-visit": "^5.0.0",
"uuid": "^9.0.1",
"valtio": "^1.12.0"
},
"devDependencies": {
"@aws-sdk/client-secrets-manager": "^3.410.0",
"@types/hast": "^2.3.4",
"@types/hast": "^3.0.4",
"@types/jsrsasign": "^10.5.12",
"@types/node": "^20.11.16",
"@types/react": "^18.2.24",
"@types/unist": "^2.0.6",
"@types/unist": "^3.0.2",
"acorn": "^8.11.3",
"api-types": "*",
"config": "*",
+3517 -111
View File
File diff suppressed because it is too large. Load diff