mirror of
https://github.com/supabase/supabase.git
synced 2026-10-18 15:49:09 +03:00
test
This commit is contained in:
1 parent
31c3ba2bc5
commit
ae3eecc9aa
17 files changed
+5365
-128
No files matched your search
@@ -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 }
|
||||
@@ -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 }
|
||||
@@ -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
@@ -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": "*",
|
||||
|
||||
Generated
+3517
-111
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user