diff --git a/apps/docs/app/api/ai/docs/route.ts b/apps/docs/app/api/ai/docs/route.ts new file mode 100644 index 0000000000000..3b1df3ee2a717 --- /dev/null +++ b/apps/docs/app/api/ai/docs/route.ts @@ -0,0 +1,80 @@ +import { SupabaseClient } from '@supabase/supabase-js' +import { ApplicationError, UserError, clippy } from 'ai-commands/edge' +import { NextRequest, NextResponse } from 'next/server' +import OpenAI from 'openai' + +export const runtime = 'edge' +/* To avoid OpenAI errors, restrict to the Vercel Edge Function regions that + overlap with the OpenAI API regions. + + Reference for Vercel regions: https://vercel.com/docs/edge-network/regions#region-list + Reference for OpenAI regions: https://help.openai.com/en/articles/5347006-openai-api-supported-countries-and-territories + */ +export const preferredRegion = [ + 'arn1', + 'bom1', + 'cdg1', + 'cle1', + 'cpt1', + 'dub1', + 'fra1', + 'gru1', + 'hnd1', + 'iad1', + 'icn1', + 'kix1', + 'lhr1', + 'pdx1', + 'sfo1', + 'sin1', + 'syd1', +] + +const openAiKey = process.env.OPENAI_API_KEY +const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL as string +const supabaseServiceKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY as string + +export async function POST(req: NextRequest) { + if (!openAiKey || !supabaseUrl || !supabaseServiceKey) { + return NextResponse.json( + { error: 'Missing environment variables for AI features.' }, + { status: 500 } + ) + } + + const openai = new OpenAI({ apiKey: openAiKey }) + const supabaseClient = new SupabaseClient(supabaseUrl, supabaseServiceKey) + + try { + const { messages } = (await req.json()) as { + messages: { content: string; role: 'user' | 'assistant' }[] + } + + if (!messages) { + throw new UserError('Missing messages in request data') + } + + const response = await clippy(openai, supabaseClient, messages) + + // Proxy the streamed SSE response from OpenAI + return new NextResponse(response.body, { + headers: { + 'Content-Type': 'text/event-stream', + }, + }) + } catch (error: unknown) { + console.error(error) + if (error instanceof UserError) { + return NextResponse.json({ error: error.message, data: error.data }, { status: 400 }) + } else if (error instanceof ApplicationError) { + console.error(`${error.message}: ${JSON.stringify(error.data)}`) + } else { + console.error(error) + } + + return NextResponse.json( + { error: 'There was an error processing your request' }, + { status: 500 } + ) + } +} diff --git a/apps/docs/app/api/crawlers/route.ts b/apps/docs/app/api/crawlers/route.ts index fb2f68fdb9c50..39f4eed592ab3 100644 --- a/apps/docs/app/api/crawlers/route.ts +++ b/apps/docs/app/api/crawlers/route.ts @@ -3,7 +3,7 @@ import { fromMarkdown } from 'mdast-util-from-markdown' import { mdxFromMarkdown } from 'mdast-util-mdx' import { toHast } from 'mdast-util-to-hast' import { mdxjs } from 'micromark-extension-mdxjs' -import { redirect } from 'next/navigation' +import { notFound } from 'next/navigation' import { visit } from 'unist-util-visit' import { REFERENCES } from '~/content/navigation.references' @@ -15,7 +15,6 @@ import { import { getRefMarkdown } from '~/features/docs/Reference.mdx' import type { MethodTypes } from '~/features/docs/Reference.typeSpec' import type { AbbrevApiReferenceSection } from '~/features/docs/Reference.utils' -import { notFoundLink } from '~/features/recommendations/NotFound.utils' import { BASE_PATH } from '~/lib/constants' export async function GET(request: Request) { @@ -52,7 +51,7 @@ export async function GET(request: Request) { } catch {} if (!section) { - redirect(notFoundLink(`${lib}/${slug}`)) + notFound() } const html = htmlShell( diff --git a/apps/docs/pages/__dev-secret-auth.tsx b/apps/docs/app/dev-secret-auth/AuthForm.client.tsx similarity index 68% rename from apps/docs/pages/__dev-secret-auth.tsx rename to apps/docs/app/dev-secret-auth/AuthForm.client.tsx index eccae335bdafd..25cc4ed1faa74 100644 --- a/apps/docs/pages/__dev-secret-auth.tsx +++ b/apps/docs/app/dev-secret-auth/AuthForm.client.tsx @@ -1,20 +1,12 @@ -import { useRouter } from 'next/compat/router' -import { Button_Shadcn_ } from 'ui' -import { auth } from '~/lib/userAuth' +'use client' -export function getServerSideProps() { - if (process.env.NEXT_PUBLIC_DEV_AUTH_PAGE === 'true') { - return { - props: {}, - } - } +import { useRouter } from 'next/navigation' - return { - notFound: true, - } -} +import { Button_Shadcn_ } from 'ui' + +import { auth } from '~/lib/userAuth' -export default function DevOnlySecretAuth() { +export function DevSecretAuthForm() { const router = useRouter() function signInWithGitHub() { @@ -37,7 +29,7 @@ export default function DevOnlySecretAuth() { } return ( -
+

Sign in

@@ -46,10 +38,10 @@ export default function DevOnlySecretAuth() { the same domain.

- + Sign in with GitHub - + Sign out diff --git a/apps/docs/app/dev-secret-auth/page.tsx b/apps/docs/app/dev-secret-auth/page.tsx new file mode 100644 index 0000000000000..54da5e984e928 --- /dev/null +++ b/apps/docs/app/dev-secret-auth/page.tsx @@ -0,0 +1,11 @@ +import { notFound } from 'next/navigation' + +import { DevSecretAuthForm } from './AuthForm.client' + +export default async function DevOnlySecretAuth() { + if (process.env.NEXT_PUBLIC_DEV_AUTH_PAGE !== 'true') { + throw notFound() + } + + return +} diff --git a/apps/docs/app/guides/ai/python/[slug]/page.tsx b/apps/docs/app/guides/ai/python/[slug]/page.tsx index 1882cf7e20c8a..578df9c311911 100644 --- a/apps/docs/app/guides/ai/python/[slug]/page.tsx +++ b/apps/docs/app/guides/ai/python/[slug]/page.tsx @@ -1,11 +1,11 @@ import { type SerializeOptions } from 'next-mdx-remote/dist/types' -import { redirect } from 'next/navigation' +import { notFound } 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' @@ -73,7 +73,7 @@ const getContent = async ({ slug }: Params) => { const page = pageMap.find(({ slug: validSlug }) => validSlug && validSlug === slug) if (!page) { - redirect(notFoundLink(`api/python/${slug}`)) + notFound() } const { remoteFile, meta } = page diff --git a/apps/docs/app/guides/cli/github-action/[slug]/page.tsx b/apps/docs/app/guides/cli/github-action/[slug]/page.tsx index 5ec1e90395206..2f229fbc8f992 100644 --- a/apps/docs/app/guides/cli/github-action/[slug]/page.tsx +++ b/apps/docs/app/guides/cli/github-action/[slug]/page.tsx @@ -1,12 +1,11 @@ import { type SerializeOptions } from 'next-mdx-remote/dist/types' -import { redirect } from 'next/navigation' +import { notFound } 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' @@ -73,7 +72,7 @@ const getContent = async ({ slug }: Params) => { const page = pageMap.find(({ slug: validSlug }) => validSlug && validSlug === slug) if (!page) { - redirect(notFoundLink(`cli/github-action/${slug}`)) + notFound() } const { remoteFile, meta } = page diff --git a/apps/docs/app/guides/graphql/[[...slug]]/page.tsx b/apps/docs/app/guides/graphql/[[...slug]]/page.tsx index 0034e6beece8c..aaf53857d1f59 100644 --- a/apps/docs/app/guides/graphql/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/graphql/[[...slug]]/page.tsx @@ -1,12 +1,11 @@ import { type SerializeOptions } from 'next-mdx-remote/dist/types' -import { redirect } from 'next/navigation' +import { notFound } 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' @@ -127,7 +126,7 @@ const getContent = async ({ slug }: Params) => { const page = pageMap.find((page) => page.slug === slug?.at(0)) if (!page) { - redirect(notFoundLink(`graphql/${slug ? slug.join('/') : ''}`)) + notFound() } const { remoteFile, meta } = page diff --git a/apps/docs/app/guides/platform/terraform/[[...slug]]/page.tsx b/apps/docs/app/guides/platform/terraform/[[...slug]]/page.tsx index f9cbcc62a0ce6..489f1d7109556 100644 --- a/apps/docs/app/guides/platform/terraform/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/platform/terraform/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ import matter from 'gray-matter' import { type SerializeOptions } from 'next-mdx-remote/dist/types' -import { redirect } from 'next/navigation' +import { notFound } 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' @@ -103,7 +103,7 @@ const getContent = async ({ slug }: Params) => { const page = pageMap.find((page) => page.slug === requestedSlug) if (!page) { - redirect(notFoundLink('platform/terraform' + (slug?.join('/') ?? ''))) + notFound() } const { meta, remoteFile, useRoot } = page diff --git a/apps/docs/app/not-found.tsx b/apps/docs/app/not-found.tsx new file mode 100644 index 0000000000000..14606cd8c1eb0 --- /dev/null +++ b/apps/docs/app/not-found.tsx @@ -0,0 +1,50 @@ +import { type Metadata } from 'next' +import Link from 'next/link' + +import { Button } from 'ui' + +import { Recommendations, SearchButton } from '~/features/recommendations/NotFound.client' +import { LayoutMainContent } from '~/layouts/DefaultLayout' +import { SidebarSkeleton } from '~/layouts/MainSkeleton' + +export default function NotFound() { + return ( + + +
+

404: We couldn't find that page

+

+ Sorry, we couldn't find that page. It might be missing, or we had a temporary error + generating it. +

+
+ + + +
+ +
+
+
+ ) +} + +export const metadata: Metadata = { + title: 'Not found', + robots: { + index: false, + }, +} diff --git a/apps/docs/app/not-found/layout.tsx b/apps/docs/app/not-found/layout.tsx deleted file mode 100644 index b34cd4356b28f..0000000000000 --- a/apps/docs/app/not-found/layout.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { type Metadata } from 'next' -import { type PropsWithChildren } from 'react' -import { LayoutMainContent } from '~/layouts/DefaultLayout' -import { SidebarSkeleton } from '~/layouts/MainSkeleton' - -const metadata: Metadata = { - title: 'Not found', - robots: { - index: false, - }, -} - -const NotFoundLayout = ({ children }: PropsWithChildren) => ( - - {children} - -) - -export default NotFoundLayout -export { metadata } diff --git a/apps/docs/app/not-found/page.tsx b/apps/docs/app/not-found/page.tsx deleted file mode 100644 index 129aa70aec94e..0000000000000 --- a/apps/docs/app/not-found/page.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { type Metadata } from 'next' -import Link from 'next/link' - -import { Button } from 'ui' - -import { SearchButton, Recommendations } from '~/features/recommendations/NotFound.client' - -const metadata: Metadata = { - robots: { - index: false, - follow: false, - }, -} - -const NotFoundPage = async ({ searchParams: { page } }: { searchParams: { page?: string } }) => { - return ( -
-

404: We couldn't find that page

-

- Sorry, we couldn't find that page. It might be missing, or we had a temporary error - generating it. -

-
- - - -
- -
- ) -} - -export default NotFoundPage -export { metadata } diff --git a/apps/docs/app/reference/[...slug]/page.tsx b/apps/docs/app/reference/[...slug]/page.tsx index 8d483ffcbf2bf..5bc1ec9fa27cd 100644 --- a/apps/docs/app/reference/[...slug]/page.tsx +++ b/apps/docs/app/reference/[...slug]/page.tsx @@ -1,4 +1,4 @@ -import { redirect } from 'next/navigation' +import { notFound } from 'next/navigation' import { REFERENCES } from '~/content/navigation.references' import { ApiReferencePage } from '~/features/docs/Reference.apiPage' @@ -11,7 +11,6 @@ import { parseReferencePath, redirectNonexistentReferenceSection, } from '~/features/docs/Reference.utils' -import { notFoundLink } from '~/features/recommendations/NotFound.utils' export default async function ReferencePage({ params: { slug }, @@ -19,7 +18,7 @@ export default async function ReferencePage({ params: { slug: Array } }) { if (!Object.keys(REFERENCES).includes(slug[0].replaceAll('-', '_'))) { - redirect(notFoundLink(slug.join('/'))) + notFound() } const parsedPath = parseReferencePath(slug) @@ -47,7 +46,7 @@ export default async function ReferencePage({ ) } else { - redirect(notFoundLink(slug.join('/'))) + notFound() } } diff --git a/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx b/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx index 6b9c1a73ab3c6..a58f00c77e480 100644 --- a/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx @@ -7,7 +7,7 @@ import { memo, useState } from 'react' import { useIsLoggedIn, useIsUserLoading } from 'common' import { Button, buttonVariants, cn } from 'ui' -import { CommandMenuTrigger, useSetCommandMenuOpen } from 'ui-patterns/CommandMenu' +import { CommandMenuTrigger } from 'ui-patterns/CommandMenu' import GlobalNavigationMenu from './GlobalNavigationMenu' const GlobalMobileMenu = dynamic(() => import('./GlobalMobileMenu')) @@ -17,7 +17,6 @@ const TopNavBar: FC = () => { const isLoggedIn = useIsLoggedIn() const isUserLoading = useIsUserLoading() const [mobileMenuOpen, setMobileMenuOpen] = useState(false) - const setCommandMenuOpen = useSetCommandMenuOpen() return ( <> @@ -93,7 +92,7 @@ const TopNavBar: FC = () => { )} {process.env.NEXT_PUBLIC_DEV_AUTH_PAGE === 'true' && ( )} diff --git a/apps/docs/features/docs/GuidesMdx.utils.tsx b/apps/docs/features/docs/GuidesMdx.utils.tsx index 49a2bf2c7fa9f..4b6061d3f3b13 100644 --- a/apps/docs/features/docs/GuidesMdx.utils.tsx +++ b/apps/docs/features/docs/GuidesMdx.utils.tsx @@ -1,13 +1,12 @@ import matter from 'gray-matter' import { type Metadata, type ResolvingMetadata } from 'next' -import { redirect } from 'next/navigation' +import { notFound } from 'next/navigation' import { readFile, readdir } from 'node:fs/promises' import { extname, join, sep } from 'node:path' import { pluckPromise } from '~/features/helpers.fn' import { cache_fullProcess_withDevCacheBust, existsFile } from '~/features/helpers.fs' import type { OrPromise } from '~/features/helpers.types' -import { notFoundLink } from '~/features/recommendations/NotFound.utils' import { generateOpenGraphImageMeta } from '~/features/seo/openGraph' import { BASE_PATH } from '~/lib/constants' import { GUIDES_DIRECTORY, isValidGuideFrontmatter, type GuideFrontmatter } from '~/lib/docs' @@ -46,14 +45,14 @@ const getGuidesMarkdownInternal = async ({ slug }: { slug: string[] }) => { !fullPath.startsWith(GUIDES_DIRECTORY) || !PUBLISHED_SECTIONS.some((section) => relPath.startsWith(section)) ) { - redirect(notFoundLink(slug.join('/'))) + notFound() } let mdx: string try { mdx = await readFile(fullPath, 'utf-8') } catch { - redirect(notFoundLink(slug.join('/'))) + notFound() } const editLink = newEditLink( diff --git a/apps/docs/features/recommendations/NotFound.client.tsx b/apps/docs/features/recommendations/NotFound.client.tsx index c575e8e94b9c2..7ff03070cb7b7 100644 --- a/apps/docs/features/recommendations/NotFound.client.tsx +++ b/apps/docs/features/recommendations/NotFound.client.tsx @@ -2,6 +2,7 @@ import { useSupabaseClient } from '@supabase/auth-helpers-react' import { type SupabaseClient } from '@supabase/supabase-js' +import { usePathname } from 'next/navigation' import { useEffect, useRef, useState } from 'react' import { useSetCommandMenuOpen } from '@ui-patterns/CommandMenu' @@ -38,29 +39,38 @@ async function getRecommendations(page: string, supabase: SupabaseClient) { } } -function Recommendations({ page }: { page: string }) { +function Recommendations() { + const pathname = usePathname() const supabase = useSupabaseClient() + + const [loading, setLoading] = useState(true) const [recommendations, setRecommendations] = useState( [] as Array> ) - const fetched = useRef(false) - useEffect(() => { - getRecommendations(page, supabase).then((data) => { - if (!fetched.current) { + if (!pathname) return + + let stale = false + + getRecommendations(pathname, supabase).then((data) => { + if (!stale) { setRecommendations(data) - fetched.current = true + setLoading(false) } }) - }, [page, supabase]) + + return () => { + stale = true + } + }, [pathname, supabase]) return (

Are you looking for...?

- {!fetched.current && } - {fetched.current && recommendations.length === 0 && } - {fetched.current && recommendations.length > 0 && ( + {loading && } + {!loading && recommendations.length === 0 && } + {!loading && recommendations.length > 0 && ( )}
diff --git a/apps/docs/features/recommendations/NotFound.utils.ts b/apps/docs/features/recommendations/NotFound.utils.ts deleted file mode 100644 index 197741508403e..0000000000000 --- a/apps/docs/features/recommendations/NotFound.utils.ts +++ /dev/null @@ -1,8 +0,0 @@ -const notFoundLink = (origPath: string) => { - if (!origPath) return '/not-found' - - const searchParams = new URLSearchParams({ page: origPath }) - return `/not-found?${searchParams}` -} - -export { notFoundLink } diff --git a/apps/docs/next-env.d.ts b/apps/docs/next-env.d.ts index fd36f9494e2c2..4f11a03dc6cc3 100644 --- a/apps/docs/next-env.d.ts +++ b/apps/docs/next-env.d.ts @@ -1,6 +1,5 @@ /// /// -/// // NOTE: This file should not be edited // see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/docs/pages/404.mdx b/apps/docs/pages/404.mdx deleted file mode 100644 index b75a4a59620f2..0000000000000 --- a/apps/docs/pages/404.mdx +++ /dev/null @@ -1,13 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: '404', - title: '404 not found', - description: '404 not found', -} - -404 not found - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/_app.tsx b/apps/docs/pages/_app.tsx deleted file mode 100644 index 38b19b776fe7d..0000000000000 --- a/apps/docs/pages/_app.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import '@code-hike/mdx/styles' -import 'config/code-hike.scss' -import '../styles/main.scss' -import '../styles/new-docs.scss' -import '../styles/prism-okaidia.scss' - -import MetaFaviconsPagesRouter from 'common/MetaFavicons/pages-router' -import Head from 'next/head' - -import type { AppPropsWithLayout } from '~/types' -import { GlobalProviders } from '~/features/app.providers' - -function MyApp({ Component, pageProps }: AppPropsWithLayout) { - return ( - <> - - - - Supabase Docs - - - - - - ) -} - -export default MyApp diff --git a/apps/docs/pages/_document.tsx b/apps/docs/pages/_document.tsx deleted file mode 100644 index 54e8bf3e2a290..0000000000000 --- a/apps/docs/pages/_document.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Html, Head, Main, NextScript } from 'next/document' - -export default function Document() { - return ( - - - -
- - - - ) -} diff --git a/apps/docs/pages/api/ai/docs.ts b/apps/docs/pages/api/ai/docs.ts deleted file mode 100644 index b2e45876a610d..0000000000000 --- a/apps/docs/pages/api/ai/docs.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { SupabaseClient } from '@supabase/supabase-js' -import { ApplicationError, UserError, clippy } from 'ai-commands/edge' -import { NextRequest } from 'next/server' -import OpenAI from 'openai' - -export const config = { - runtime: 'edge', - /* To avoid OpenAI errors, restrict to the Vercel Edge Function regions that - overlap with the OpenAI API regions. - - Reference for Vercel regions: https://vercel.com/docs/edge-network/regions#region-list - Reference for OpenAI regions: https://help.openai.com/en/articles/5347006-openai-api-supported-countries-and-territories - */ - regions: [ - 'arn1', - 'bom1', - 'cdg1', - 'cle1', - 'cpt1', - 'dub1', - 'fra1', - 'gru1', - 'hnd1', - 'iad1', - 'icn1', - 'kix1', - 'lhr1', - 'pdx1', - 'sfo1', - 'sin1', - 'syd1', - ], -} - -const openAiKey = process.env.OPENAI_API_KEY -const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL as string -const supabaseServiceKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY as string - -export default async function handler(req: NextRequest) { - if (!openAiKey) { - return new Response( - JSON.stringify({ - error: 'No OPENAI_API_KEY set. Create this environment variable to use AI features.', - }), - { - status: 500, - headers: { 'Content-Type': 'application/json' }, - } - ) - } - - if (!supabaseUrl) { - return new Response( - JSON.stringify({ - error: - 'No NEXT_PUBLIC_SUPABASE_URL set. Create this environment variable to use AI features.', - }), - { - status: 500, - headers: { 'Content-Type': 'application/json' }, - } - ) - } - - if (!supabaseServiceKey) { - return new Response( - JSON.stringify({ - error: - 'No NEXT_PUBLIC_SUPABASE_ANON_KEY set. Create this environment variable to use AI features.', - }), - { - status: 500, - headers: { 'Content-Type': 'application/json' }, - } - ) - } - - const { method } = req - - switch (method) { - case 'POST': - return handlePost(req) - default: - return new Response( - JSON.stringify({ data: null, error: { message: `Method ${method} Not Allowed` } }), - { - status: 405, - headers: { 'Content-Type': 'application/json', Allow: 'POST' }, - } - ) - } -} - -async function handlePost(request: NextRequest) { - const openai = new OpenAI({ apiKey: openAiKey }) - - const body = await (request.json() as Promise<{ - messages: { content: string; role: 'user' | 'assistant' }[] - }>) - - const { messages } = body - - if (!messages) { - throw new UserError('Missing messages in request data') - } - - const supabaseClient = new SupabaseClient(supabaseUrl, supabaseServiceKey) - - try { - const response = await clippy(openai, supabaseClient, messages) - - // Proxy the streamed SSE response from OpenAI - return new Response(response.body, { - headers: { - 'Content-Type': 'text/event-stream', - }, - }) - } catch (error: unknown) { - console.error(error) - if (error instanceof UserError) { - return new Response( - JSON.stringify({ - error: error.message, - data: error.data, - }), - { - status: 400, - headers: { 'Content-Type': 'application/json' }, - } - ) - } else if (error instanceof ApplicationError) { - // Print out application errors with their additional data - console.error(`${error.message}: ${JSON.stringify(error.data)}`) - } else { - // Print out unexpected errors as is to help with debugging - console.error(error) - } - - // TODO: include more response info in debug environments - return new Response( - JSON.stringify({ - error: 'There was an error processing your request', - }), - { - status: 500, - headers: { 'Content-Type': 'application/json' }, - } - ) - } -} diff --git a/apps/docs/pages/faq.mdx b/apps/docs/pages/faq.mdx deleted file mode 100644 index c9819c9f6a434..0000000000000 --- a/apps/docs/pages/faq.mdx +++ /dev/null @@ -1,44 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'faq', - title: 'FAQs', - description: 'Most frequently asked questions regarding Supabase', -} - -### Where do I find support? - -Choose the support channel relevant for your situation here: [supabase.com/support](https://supabase.com/support) - -### How much does it cost? - -Self-hosting Supabase is free. If you wish to use our cloud-platform, we provide [simple, predictable pricing](https://supabase.com/pricing). - -### How do I host Supabase? - -You can use the docker compose script [here](https://github.com/supabase/supabase/tree/master/docker), and find detailed instructions [here](/docs/guides/hosting/overview). - -Supabase is an amalgamation of open source tools. Some of these tools are made by Supabase (like our [Realtime Server](https://github.com/supabase/realtime)), some we support directly (like [PostgREST](http://postgrest.org/en/v7.0.0/)), and some are third-party tools (like [KonSupabase is an amalgamation open sourceg](https://github.com/Kong/kong)). - -All of the tools we use in Supabase are MIT, Apache 2.0, or PostgreSQL licensed. This is one of the requirements to be considered for the Supabase stack. - -### How can you be a Firebase alternative if you're built with a relational database? - -We started Supabase because we love the functionality of Firebase, but we personally experienced the scaling issues that many others experienced. We chose Postgres because it's well-trusted, with phenomenal scalability. - -Our goal is to make Postgres as easy to use as Firebase, so that you no longer have to choose between usability and scalability. -We're sure that once you start using Postgres, you'll love it more than any other database. - -### Do you support `[some other database]`? - -We only support PostgreSQL. It's unlikely we'll ever move away from Postgres; however, you can [vote on a new database](https://github.com/supabase/supabase/discussions/6) if you want us to start development. - -### Do you have a library for `[some other language]`? - -We officially support [JavaScript](/docs/reference/javascript/installing), [Swift](/docs/reference/swift/installing), and [Flutter](/docs/reference/dart/installing). - -You can find community-supported libraries including Python, C#, PHP, and Ruby in our [GitHub Community](https://github.com/supabase-community), and you can also help us to identify the most popular languages by [voting for a new client library](https://github.com/supabase/supabase/discussions/5). - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/scripts/search/sources/index.ts b/apps/docs/scripts/search/sources/index.ts index 99bf19a3fd31d..d4110d95afb1f 100644 --- a/apps/docs/scripts/search/sources/index.ts +++ b/apps/docs/scripts/search/sources/index.ts @@ -93,18 +93,11 @@ export async function fetchSources() { 'spec/common-cli-sections.json' ).load() - const pagesSources = (await walk('pages')) - .filter(({ path }) => /\.mdx?$/.test(path)) - .filter(({ path }) => !ignoredFiles.includes(path)) - .map((entry) => new MarkdownLoader('guide', entry.path).load()) - - const contentSources = (await walk('content')) + const guideSources = (await walk('content')) .filter(({ path }) => /\.mdx?$/.test(path)) .filter(({ path }) => !ignoredFiles.includes(path)) .map((entry) => new MarkdownLoader('guide', entry.path, { yaml: true }).load()) - const guideSources = [...pagesSources, ...contentSources] - const partnerIntegrationSources = (await fetchPartners()).map((partner) => new IntegrationLoader(partner.slug, partner).load() )