mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
refactor: split guide layouts by section (#32694)
Split guide layouts by section, so that nav data fetches can be pushed down to thesection where they're actually used. Limits blast radius of any data fetch problems, and also lets us push down client components in the future, since layouts now have context on the request path and we don't need a high-level usePathname.
This commit is contained in:
1 parent
8a3d147afc
commit
44db2d3f16
60 files changed
+585
-755
No files matched your search
@@ -1,21 +0,0 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
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 }
|
||||
@@ -1,61 +0,0 @@
|
||||
import { cache, type PropsWithChildren } from 'react'
|
||||
|
||||
import { IS_PLATFORM } from 'common'
|
||||
|
||||
import { NavMenuSection } from '~/components/Navigation/Navigation.types'
|
||||
import Layout from '~/layouts/guides'
|
||||
import { supabaseMisc } from '~/lib/supabaseMisc'
|
||||
import { getAiPrompts } from './getting-started/ai-prompts/[slug]/AiPrompts.utils'
|
||||
|
||||
// Revalidate occasionally to pick up changes to partners
|
||||
// 60 seconds/minute * 60 minutes/hour * 24 hours/day
|
||||
// export const revalidate = 86_400
|
||||
|
||||
const GuidesLayout = async ({ children }: PropsWithChildren) => {
|
||||
const [partnerNavItems, promptNavItems] = await Promise.all([getPartners(), getPrompts()])
|
||||
|
||||
const additionalNavItems =
|
||||
partnerNavItems.length > 0 || promptNavItems.length > 0
|
||||
? { integrations: partnerNavItems, prompts: promptNavItems }
|
||||
: undefined
|
||||
|
||||
return <Layout additionalNavItems={additionalNavItems}>{children}</Layout>
|
||||
}
|
||||
|
||||
async function getPrompts() {
|
||||
const prompts = await getAiPrompts()
|
||||
return prompts.map(
|
||||
(prompt) =>
|
||||
({
|
||||
name: prompt.heading,
|
||||
url: `/guides/getting-started/ai-prompts/${prompt.filename}`,
|
||||
}) as Partial<NavMenuSection>
|
||||
)
|
||||
}
|
||||
|
||||
const getPartners = cache(getPartnersImpl)
|
||||
async function getPartnersImpl() {
|
||||
if (!IS_PLATFORM) return []
|
||||
|
||||
const { data, error } = await supabaseMisc()
|
||||
.from('partners')
|
||||
.select('slug, title')
|
||||
.eq('approved', true)
|
||||
.eq('type', 'technology')
|
||||
.order('title')
|
||||
if (error) {
|
||||
console.error(new Error('Error fetching partners', { cause: error }))
|
||||
}
|
||||
|
||||
const partnerNavItems = (data ?? []).map(
|
||||
(partner) =>
|
||||
({
|
||||
name: partner.title,
|
||||
url: `https://supabase.com/partners/integrations/${partner.slug}` as `https://${string}`,
|
||||
}) as Partial<NavMenuSection>
|
||||
)
|
||||
|
||||
return partnerNavItems
|
||||
}
|
||||
|
||||
export default GuidesLayout
|
||||
@@ -1,106 +0,0 @@
|
||||
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' with { 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 }
|
||||
@@ -1,146 +0,0 @@
|
||||
import matter from 'gray-matter'
|
||||
import { type SerializeOptions } from 'next-mdx-remote/dist/types'
|
||||
import { notFound } from 'next/navigation'
|
||||
import rehypeSlug from 'rehype-slug'
|
||||
|
||||
import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
|
||||
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
|
||||
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) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
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(/<!--.*?-->/, '')
|
||||
let { content, data } = matter(rawContent)
|
||||
|
||||
// Remove the title from the content so it isn't duplicated in the final display
|
||||
content = removeRedundantH1(content)
|
||||
|
||||
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 }
|
||||
@@ -1,410 +0,0 @@
|
||||
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 }
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* 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,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const AiGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['ai', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('ai')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['ai', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default AiGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function AiLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
+2
@@ -10,6 +10,8 @@ import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLin
|
||||
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
|
||||
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
// We fetch these docs at build time from an external repo
|
||||
const org = 'supabase'
|
||||
const repo = 'vecs'
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const ApiGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['api', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('api')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['api', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default ApiGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function ApiLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const AuthGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['auth', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('auth')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['auth', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default AuthGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function AuthLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const CronGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['cron', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('cron')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['cron', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default CronGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function CronLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const DatabaseGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['database', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('database')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['database', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default DatabaseGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
File renamed without changes.
+2
@@ -18,6 +18,8 @@ import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
|
||||
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
|
||||
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
// We fetch these docs at build time from an external repo
|
||||
const org = 'supabase'
|
||||
const repo = 'wrappers'
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function DatabaseLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const DeploymentGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['deployment', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('deployment')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['deployment', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default DeploymentGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
+2
@@ -11,6 +11,8 @@ import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
|
||||
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
|
||||
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
// We fetch these docs at build time from an external repo
|
||||
const org = 'supabase'
|
||||
const repo = 'setup-cli'
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function DeploymentLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
+2
@@ -18,6 +18,8 @@ import {
|
||||
terraformDocsRepo,
|
||||
} from '../terraformConstants'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
// Each external docs page is mapped to a local page
|
||||
const pageMap = [
|
||||
{
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const FunctionsGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['functions', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('functions')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['functions', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default FunctionsGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function FunctionsLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const GettingStartedGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['getting-started', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('getting-started')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['getting-started', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default GettingStartedGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,20 @@
|
||||
import { type NavMenuSection } from '~/components/Navigation/Navigation.types'
|
||||
import Layout from '~/layouts/guides'
|
||||
import { getAiPrompts } from '../getting-started/ai-prompts/[slug]/AiPrompts.utils'
|
||||
|
||||
export default async function GettingStartedLayout({ children }: { children: React.ReactNode }) {
|
||||
const additionalNavItems = { prompts: await getPrompts() }
|
||||
|
||||
return <Layout additionalNavItems={additionalNavItems}>{children}</Layout>
|
||||
}
|
||||
|
||||
async function getPrompts() {
|
||||
const prompts = await getAiPrompts()
|
||||
return prompts.map(
|
||||
(prompt) =>
|
||||
({
|
||||
name: prompt.heading,
|
||||
url: `/guides/getting-started/ai-prompts/${prompt.filename}`,
|
||||
}) as Partial<NavMenuSection>
|
||||
)
|
||||
}
|
||||
+2
@@ -11,6 +11,8 @@ import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
|
||||
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
|
||||
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
// We fetch these docs at build time from an external repo
|
||||
const org = 'supabase'
|
||||
const repo = 'pg_graphql'
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function GraphQlLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const IntegrationsGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['integrations', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('integrations')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['integrations', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default IntegrationsGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,39 @@
|
||||
import { IS_PLATFORM } from 'common'
|
||||
import { unstable_cache } from 'next/cache'
|
||||
|
||||
import { type NavMenuSection } from '~/components/Navigation/Navigation.types'
|
||||
import Layout from '~/layouts/guides'
|
||||
import { supabaseMisc } from '~/lib/supabaseMisc'
|
||||
|
||||
export default async function IntegrationsLayout({ children }: { children: React.ReactNode }) {
|
||||
const additionalNavItems = { integrations: await getPartners() }
|
||||
|
||||
return <Layout additionalNavItems={additionalNavItems}>{children}</Layout>
|
||||
}
|
||||
|
||||
// Will need to turn on revalidation later, just turning it off for now so we
|
||||
// can slowly turn things back on while monitoring usage
|
||||
const getPartners = unstable_cache(getPartnersImpl, [], { revalidate: false })
|
||||
async function getPartnersImpl() {
|
||||
if (!IS_PLATFORM) return []
|
||||
|
||||
const { data, error } = await supabaseMisc()
|
||||
.from('partners')
|
||||
.select('slug, title')
|
||||
.eq('approved', true)
|
||||
.eq('type', 'technology')
|
||||
.order('title')
|
||||
if (error) {
|
||||
console.error(new Error('Error fetching partners', { cause: error }))
|
||||
}
|
||||
|
||||
const partnerNavItems = (data ?? []).map(
|
||||
(partner) =>
|
||||
({
|
||||
name: partner.title,
|
||||
url: `https://supabase.com/partners/integrations/${partner.slug}` as `https://${string}`,
|
||||
}) as Partial<NavMenuSection>
|
||||
)
|
||||
|
||||
return partnerNavItems
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const LocalDevelopmentGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['local-development', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('local-development')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['local-development', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default LocalDevelopmentGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
File renamed without changes.
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function LocalDevelopmentLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const MonitoringTroubleshootingGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['monitoring-troubleshooting', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('monitoring-troubleshooting')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['monitoring-troubleshooting', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default MonitoringTroubleshootingGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,9 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function MonitoringTroubleshootingLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const PlatformGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['platform', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('platform')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['platform', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default PlatformGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function PlatformLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const QueuesGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['queues', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('queues')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['queues', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default QueuesGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function QueuesLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const RealtimeGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['realtime', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('realtime')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['realtime', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default RealtimeGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function RealtimeLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const ResourcesGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['resources', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('resources')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['resources', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default ResourcesGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function ResourcesLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const SelfHostingGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['self-hosting', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('self-hosting')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['self-hosting', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default SelfHostingGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function SelfHostingLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getGuidesMarkdown,
|
||||
genGuideMeta,
|
||||
genGuidesStaticParams,
|
||||
} from '~/features/docs/GuidesMdx.utils'
|
||||
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
|
||||
|
||||
export const dynamicParams = false
|
||||
|
||||
type Params = { slug?: string[] }
|
||||
|
||||
const StorageGuidePage = async ({ params }: { params: Params }) => {
|
||||
const slug = ['storage', ...(params.slug ?? [])]
|
||||
const data = await getGuidesMarkdown(slug)
|
||||
|
||||
return <GuideTemplate {...data!} />
|
||||
}
|
||||
|
||||
const generateStaticParams = genGuidesStaticParams('storage')
|
||||
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
|
||||
getGuidesMarkdown(['storage', ...(params.slug ?? [])])
|
||||
)
|
||||
|
||||
export default StorageGuidePage
|
||||
export { generateStaticParams, generateMetadata }
|
||||
@@ -0,0 +1,5 @@
|
||||
import Layout from '~/layouts/guides'
|
||||
|
||||
export default async function StorageLayout({ children }: { children: React.ReactNode }) {
|
||||
return <Layout>{children}</Layout>
|
||||
}
|
||||
@@ -42,7 +42,7 @@ const PUBLISHED_SECTIONS = [
|
||||
'storage',
|
||||
] as const
|
||||
|
||||
const getGuidesMarkdownInternal = async ({ slug }: { slug: string[] }) => {
|
||||
const getGuidesMarkdownInternal = async (slug: string[]) => {
|
||||
const relPath = slug.join(sep).replace(/\/$/, '')
|
||||
const fullPath = join(GUIDES_DIRECTORY, relPath + '.mdx')
|
||||
/**
|
||||
@@ -60,6 +60,7 @@ const getGuidesMarkdownInternal = async ({ slug }: { slug: string[] }) => {
|
||||
try {
|
||||
mdx = await readFile(fullPath, 'utf-8')
|
||||
} catch {
|
||||
console.error('Error reading Markdown at path: %s', fullPath)
|
||||
notFound()
|
||||
}
|
||||
|
||||
@@ -96,6 +97,9 @@ const genGuidesStaticParams = (directory?: string) => async () => {
|
||||
? (await readdir(join(GUIDES_DIRECTORY, directory), { recursive: true }))
|
||||
.filter((file) => extname(file) === '.mdx' && !file.split(sep).at(-1).startsWith('_'))
|
||||
.map((file) => ({ slug: file.replace(/\.mdx$/, '').split(sep) }))
|
||||
.concat(
|
||||
(await existsFile(join(GUIDES_DIRECTORY, `${directory}.mdx`))) ? [{ slug: [] }] : []
|
||||
)
|
||||
: PUBLISHED_SECTIONS.map(async (section) =>
|
||||
(await readdir(join(GUIDES_DIRECTORY, section), { recursive: true }))
|
||||
.filter((file) => extname(file) === '.mdx' && !file.split(sep).at(-1).startsWith('_'))
|
||||
|
||||
@@ -7,7 +7,7 @@ import remarkGfm from 'remark-gfm'
|
||||
import rehypeKatex from 'rehype-katex'
|
||||
import remarkMath from 'remark-math'
|
||||
|
||||
import { AiPromptsIndex } from '~/app/guides/(with-sidebar)/getting-started/ai-prompts/[slug]/AiPromptsIndex'
|
||||
import { AiPromptsIndex } from '~/app/guides/getting-started/ai-prompts/[slug]/AiPromptsIndex'
|
||||
import { preprocessMdxWithDefaults } from '~/features/directives/utils'
|
||||
import { components } from '~/features/docs/MdxBase.shared'
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ let octokitInstance: Octokit
|
||||
|
||||
function octokit() {
|
||||
if (!octokitInstance) {
|
||||
// https://github.com/gr2m/universal-github-app-jwt?tab=readme-ov-file#converting-pkcs1-to-pkcs8
|
||||
const privateKeyPkcs8 = crypto
|
||||
.createPrivateKey(process.env.DOCS_GITHUB_APP_PRIVATE_KEY)
|
||||
.export({
|
||||
|
||||
Reference in new issue
Block a user