mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Revert "feat: display api urls and keys instead of placeholders in do… (#20302)
Revert "feat: display api urls and keys instead of placeholders in docs (#19672)"
This reverts commit 0e8ee2e890.
This commit is contained in:
1 parent
c9a17980bd
commit
10da8f3f31
29 files changed
+110
-13671
No files matched your search
@@ -1,16 +1,58 @@
|
||||
import { useTheme } from 'next-themes'
|
||||
import Image from 'next/legacy/image'
|
||||
import Link from 'next/link'
|
||||
import { FC } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FC, useState } from 'react'
|
||||
import { Button, IconCommand, IconGitHub, IconSearch, SearchButton } from 'ui'
|
||||
import { useIsLoggedIn, useIsUserLoading } from 'common'
|
||||
|
||||
import { getPageType } from '~/lib/helpers'
|
||||
import { REFERENCES } from './NavigationMenu.constants'
|
||||
import ThemeToggle from '@ui/components/ThemeProvider/ThemeToggle'
|
||||
|
||||
const TopNavBar: FC = () => {
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const isUserLoading = useIsUserLoading()
|
||||
const { resolvedTheme } = useTheme()
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||
|
||||
const { asPath, push } = useRouter()
|
||||
const pathSegments = asPath.split('/')
|
||||
|
||||
const library = pathSegments.length >= 3 ? pathSegments[2] : undefined
|
||||
const libraryMeta = REFERENCES?.[library] ?? undefined
|
||||
const versions = libraryMeta?.versions ?? []
|
||||
|
||||
const version = versions.includes(pathSegments[pathSegments.indexOf(library) + 1])
|
||||
? pathSegments[pathSegments.indexOf(library) + 1]
|
||||
: versions[0]
|
||||
|
||||
const pageType = getPageType(asPath)
|
||||
|
||||
const pageLinks = [
|
||||
{ text: 'Guides', key: 'docs', link: '/' },
|
||||
{ text: 'Reference', key: 'reference', link: '/reference' },
|
||||
]
|
||||
|
||||
const onSelectVersion = (version: string) => {
|
||||
// [Joshen] Ideally we use <Link> but this works for now
|
||||
if (!library) return
|
||||
if (version === versions[0]) {
|
||||
push(`/reference/${library}`)
|
||||
} else {
|
||||
push(`/reference/${library}/${version}`)
|
||||
}
|
||||
}
|
||||
|
||||
// [Joshen] Kaizen: Use UI library's SidePanel for this
|
||||
const toggleMobileMenu = () => {
|
||||
setMobileMenuOpen(!mobileMenuOpen)
|
||||
const sidebar = document.querySelector('.sidebar-menu-container')
|
||||
const contentPane = document.querySelector('.main-content-pane')
|
||||
|
||||
sidebar.classList.toggle('hidden')
|
||||
contentPane.classList.toggle('hidden')
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="h-[60px] border-b backdrop-blur backdrop-filter bg bg-opacity-75">
|
||||
@@ -76,11 +118,6 @@ const TopNavBar: FC = () => {
|
||||
</a>
|
||||
</Button>
|
||||
)}
|
||||
{process.env.NEXT_PUBLIC_DEV_AUTH_PAGE === 'true' && (
|
||||
<Button asChild>
|
||||
<Link href="/__dev-secret-auth">Dev-only secret signin</Link>
|
||||
</Button>
|
||||
)}
|
||||
<Link
|
||||
href="https://github.com/supabase/supabase"
|
||||
target="_blank"
|
||||
|
||||
@@ -1,451 +0,0 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { Check, ChevronsUpDown } from 'lucide-react'
|
||||
import {
|
||||
Button_Shadcn_ as Button,
|
||||
Popover_Shadcn_ as Popover,
|
||||
PopoverTrigger_Shadcn_ as PopoverTrigger,
|
||||
PopoverContent_Shadcn_ as PopoverContent,
|
||||
Command_Shadcn_ as Command,
|
||||
CommandInput_Shadcn_ as CommandInput,
|
||||
CommandEmpty_Shadcn_ as CommandEmpty,
|
||||
CommandItem_Shadcn_ as CommandItem,
|
||||
CommandGroup_Shadcn_ as CommandGroup,
|
||||
Input_Shadcn_ as Input,
|
||||
cn,
|
||||
IconCopy,
|
||||
IconCheck,
|
||||
ScrollArea,
|
||||
} from 'ui'
|
||||
import CopyToClipboard from 'react-copy-to-clipboard'
|
||||
import { proxy, useSnapshot } from 'valtio'
|
||||
import { LOCAL_STORAGE_KEYS, retrieve, store } from '~/lib/storage'
|
||||
import Link from 'next/link'
|
||||
import { useIsLoggedIn, useIsUserLoading } from 'common'
|
||||
import { ProjectsData, useProjectsQuery } from '~/lib/fetch/projects'
|
||||
import { OrganizationsData, useOrganizationsQuery } from '~/lib/fetch/organizations'
|
||||
import { BranchesData, useAllProjectsBranchesQuery } from '~/lib/fetch/branches'
|
||||
import { useProjectApiQuery } from '~/lib/fetch/projectApi'
|
||||
import { useOnLogout } from '~/lib/userAuth'
|
||||
|
||||
interface InstanceData {
|
||||
organization: OrganizationsData[number] | null
|
||||
project: ProjectsData[number]
|
||||
branch?: BranchesData[number]
|
||||
}
|
||||
|
||||
type InstanceKey = readonly [string | null, string, string | null]
|
||||
|
||||
function toInstanceKey(instance: InstanceData | null) {
|
||||
if (!instance) return null
|
||||
|
||||
return [
|
||||
instance.organization?.id ?? null,
|
||||
// @ts-ignore -- problem with OpenAPI spec that codegen reads from
|
||||
instance.project.ref as string,
|
||||
instance.branch?.id ?? null,
|
||||
] satisfies InstanceKey
|
||||
}
|
||||
|
||||
function getInstanceDataFromKey(validInstances: InstanceData[], instanceKey: InstanceKey | null) {
|
||||
if (!instanceKey || instanceKey[0] === null) return null
|
||||
|
||||
return (
|
||||
validInstances.find(
|
||||
(instance) =>
|
||||
instance.organization?.id === instanceKey[0] &&
|
||||
// @ts-ignore -- problem with OpenAPI spec that codegen reads from
|
||||
instance.project.ref === instanceKey[1] &&
|
||||
(instanceKey[2] === null || instance.branch?.id === instanceKey[2])
|
||||
) ?? null
|
||||
)
|
||||
}
|
||||
|
||||
function toStoredString(instanceKey: InstanceKey | null) {
|
||||
return JSON.stringify(instanceKey)
|
||||
}
|
||||
|
||||
function fromStoredString(maybeInstanceKey: string) {
|
||||
try {
|
||||
return JSON.parse(maybeInstanceKey)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function toPrettyString(instance: InstanceData | null) {
|
||||
if (!instance) return ''
|
||||
|
||||
const { organization, project, branch = null } = instance
|
||||
return `${organization ? `${organization.name} / ` : ''}${project.name}${
|
||||
branch ? ` / ${branch.name}` : ''
|
||||
}`
|
||||
}
|
||||
|
||||
/**
|
||||
* The Command component uses the `value` for both search matching and selection.
|
||||
* Because the two are conflated, the value must contain both:
|
||||
* 1. The organization/project/branch refs, for uniquely identifying the project
|
||||
* 2. The organization/project/branch name, so combobox search works as expected.
|
||||
*/
|
||||
function toCommandValue(instance: InstanceData) {
|
||||
const instanceKey = toInstanceKey(instance)
|
||||
const prettyString = toPrettyString(instance)
|
||||
return JSON.stringify([instanceKey, prettyString])
|
||||
}
|
||||
|
||||
function getKeyFromCommandValue(maybeInstance: string) {
|
||||
try {
|
||||
const instance = JSON.parse(maybeInstance)
|
||||
// For maximum safety, should check that the structure matches rather than casting,
|
||||
// but we can avoid that complexity as long as this is only ever used to decode
|
||||
// values encoded by `toCommandValue`
|
||||
return instance[0] as InstanceKey
|
||||
} catch {
|
||||
console.error('Failed to parse instance key from command value:', maybeInstance)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const instanceStore = proxy({
|
||||
selectedInstanceKey: null as InstanceKey | null,
|
||||
setSelectedInstanceKey: (instanceKey: InstanceKey | null) => {
|
||||
instanceStore.selectedInstanceKey = instanceKey
|
||||
if (instanceKey !== null) {
|
||||
store('local', LOCAL_STORAGE_KEYS.SAVED_ORG_PROJECT_BRANCH, toStoredString(instanceKey))
|
||||
}
|
||||
},
|
||||
clear: () => {
|
||||
instanceStore.setSelectedInstanceKey(null)
|
||||
},
|
||||
})
|
||||
|
||||
type Variable = 'url' | 'anonKey'
|
||||
|
||||
const prettyFormatVariable: Record<Variable, string> = {
|
||||
url: 'Project URL',
|
||||
anonKey: 'Anon key',
|
||||
}
|
||||
|
||||
type ProjectConfigVariablesState =
|
||||
| 'userLoading'
|
||||
| 'loggedOut'
|
||||
| 'loggedIn.dataPending'
|
||||
| 'loggedIn.hasData'
|
||||
| 'loggedIn.hasNoData'
|
||||
| 'loggedIn.dataError'
|
||||
|
||||
type ProjectConfigVariablesPlusApiState =
|
||||
| Omit<ProjectConfigVariablesState, 'loggedIn.hasData'>
|
||||
| 'loggedIn.hasData.apiDataPending'
|
||||
| 'loggedIn.hasData.apiDataSuccess'
|
||||
| 'loggedIn.hasData.apiDataError'
|
||||
|
||||
function ComboBox({
|
||||
parentStateSummary,
|
||||
instances,
|
||||
className,
|
||||
}: {
|
||||
parentStateSummary: ProjectConfigVariablesState
|
||||
instances: InstanceData[]
|
||||
className?: string
|
||||
}) {
|
||||
const { selectedInstanceKey, setSelectedInstanceKey } = useSnapshot(instanceStore)
|
||||
const selectedInstance = getInstanceDataFromKey(instances, selectedInstanceKey)
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<div className={cn('flex items-center', className)}>
|
||||
<PopoverTrigger asChild aria-label={`Select project`}>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className="overflow-hidden h-auto min-h-10 flex justify-between border-none px-1 text-left"
|
||||
>
|
||||
{parentStateSummary === 'userLoading' || parentStateSummary === 'loggedIn.dataPending'
|
||||
? 'Loading...'
|
||||
: toPrettyString(selectedInstance) || 'Select a project...'}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
</div>
|
||||
<PopoverContent className="z-[999999] p-0" side="bottom">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search project..." className="border-none ring-0" />
|
||||
<CommandEmpty>No project found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
<ScrollArea className={instances.length > 10 ? 'h-[280px]' : ''}>
|
||||
{instances.map((instance) => (
|
||||
<CommandItem
|
||||
key={toStoredString(toInstanceKey(instance))}
|
||||
value={toCommandValue(instance)}
|
||||
onSelect={(selectedValue: string) => {
|
||||
const instanceKey = getKeyFromCommandValue(selectedValue)
|
||||
setSelectedInstanceKey(instanceKey)
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
'mr-2 h-4 w-4',
|
||||
instance === selectedInstance ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
/>
|
||||
{toPrettyString(instance)}
|
||||
</CommandItem>
|
||||
))}
|
||||
</ScrollArea>
|
||||
</CommandGroup>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
function useCopy() {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
function handleCopy() {
|
||||
setCopied(true)
|
||||
setTimeout(() => {
|
||||
setCopied(false)
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
return { copied, handleCopy }
|
||||
}
|
||||
|
||||
function ProjectConfigVariablesView({
|
||||
parentStateSummary,
|
||||
variable,
|
||||
instances,
|
||||
}: {
|
||||
parentStateSummary: ProjectConfigVariablesState
|
||||
variable: Variable
|
||||
instances: InstanceData[]
|
||||
}) {
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const { selectedInstanceKey } = useSnapshot(instanceStore)
|
||||
|
||||
const selectedInstance = getInstanceDataFromKey(instances, selectedInstanceKey)
|
||||
const projectRef = selectedInstance?.branch
|
||||
? selectedInstance?.branch?.project_ref
|
||||
: // @ts-ignore -- problem with OpenAPI spec that codegen reads from
|
||||
selectedInstance?.project.ref
|
||||
const {
|
||||
data: apiData,
|
||||
isPending: apiIsPending,
|
||||
isSuccess: apiIsSuccess,
|
||||
} = useProjectApiQuery(
|
||||
{
|
||||
projectRef,
|
||||
},
|
||||
{ enabled: isLoggedIn && !!projectRef }
|
||||
)
|
||||
|
||||
const { copied, handleCopy } = useCopy()
|
||||
|
||||
const apiStateSummary: ProjectConfigVariablesPlusApiState =
|
||||
parentStateSummary !== 'loggedIn.hasData'
|
||||
? parentStateSummary
|
||||
: apiIsPending
|
||||
? 'loggedIn.hasData.apiDataPending'
|
||||
: apiIsSuccess
|
||||
? 'loggedIn.hasData.apiDataSuccess'
|
||||
: 'loggedIn.hasData.apiDataError'
|
||||
|
||||
let variableValue: string = null
|
||||
if (apiIsSuccess) {
|
||||
switch (variable) {
|
||||
case 'url':
|
||||
variableValue = `${apiData.autoApiService.protocol || 'https'}://${
|
||||
apiData.autoApiService.endpoint
|
||||
}`
|
||||
break
|
||||
case 'anonKey':
|
||||
variableValue = apiData.autoApiService.defaultApiKey
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-[min(100%, 500px)] my-4">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<span className="grow-[100]">{prettyFormatVariable[variable]}</span>
|
||||
{(parentStateSummary === 'userLoading' ||
|
||||
parentStateSummary === 'loggedIn.dataPending' ||
|
||||
parentStateSummary === 'loggedIn.hasData') && (
|
||||
<div className="flex items-center justify-between grow max-w-full gap-2">
|
||||
<ComboBox parentStateSummary={parentStateSummary} instances={instances} />
|
||||
<CopyToClipboard text={variableValue ?? ''}>
|
||||
<Button
|
||||
disabled={!variableValue}
|
||||
variant="ghost"
|
||||
className="px-0"
|
||||
onClick={handleCopy}
|
||||
aria-label="Copy"
|
||||
>
|
||||
{copied ? <IconCheck /> : <IconCopy />}
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Input
|
||||
disabled
|
||||
type="text"
|
||||
className="font-mono"
|
||||
value={
|
||||
apiStateSummary === 'userLoading' ||
|
||||
apiStateSummary === 'loggedIn.dataPending' ||
|
||||
apiStateSummary === 'loggedIn.hasData.apiDataPending'
|
||||
? 'Loading...'
|
||||
: apiStateSummary === 'loggedIn.hasData.apiDataSuccess'
|
||||
? variableValue
|
||||
: `YOUR ${prettyFormatVariable[variable].toUpperCase()}`
|
||||
}
|
||||
/>
|
||||
{parentStateSummary === 'loggedOut' && (
|
||||
<p className="text-foreground-muted text-sm mt-2 mb-0 ml-1">
|
||||
There was a problem getting your {prettyFormatVariable[variable]}. Are you{' '}
|
||||
<Link
|
||||
className="text-foreground-muted"
|
||||
href="https://supabase.com/dashboard"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
logged in
|
||||
</Link>
|
||||
?
|
||||
</p>
|
||||
)}
|
||||
{(parentStateSummary === 'loggedIn.hasNoData' ||
|
||||
parentStateSummary === 'loggedIn.dataError' ||
|
||||
apiStateSummary === 'loggedIn.hasData.apiDataError') && (
|
||||
<>
|
||||
<p className="text-foreground-muted text-sm mt-2 mb-0 ml-1">
|
||||
There was a problem getting your {prettyFormatVariable[variable]}.{' '}
|
||||
{parentStateSummary === 'loggedIn.hasNoData' && (
|
||||
<>
|
||||
Do you have{' '}
|
||||
<Link
|
||||
className="text-foreground-muted"
|
||||
href="https://supabase.com/dashboard"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
any projects
|
||||
</Link>
|
||||
?
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
{(parentStateSummary === 'loggedIn.dataError' ||
|
||||
apiStateSummary === 'loggedIn.hasData.apiDataError') && (
|
||||
<p className="text-foreground-muted text-sm mt-0 ml-1">
|
||||
You can also try looking up the value in the{' '}
|
||||
<Link
|
||||
className="text-foreground-muted"
|
||||
href="https://supabase.com/dashboard/project/_/settings/api"
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
dashboard
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function ProjectConfigVariables({ variable }: { variable: Variable }) {
|
||||
const isUserLoading = useIsUserLoading()
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
|
||||
const {
|
||||
selectedInstanceKey,
|
||||
setSelectedInstanceKey,
|
||||
clear: clearSharedStoreData,
|
||||
} = useSnapshot(instanceStore)
|
||||
const {
|
||||
data: organizations,
|
||||
isPending: organizationsIsPending,
|
||||
isError: organizationsIsError,
|
||||
} = useOrganizationsQuery({ enabled: isLoggedIn })
|
||||
const {
|
||||
data: projects,
|
||||
isPending: projectsIsPending,
|
||||
isError: projectsIsError,
|
||||
} = useProjectsQuery({ enabled: isLoggedIn })
|
||||
const {
|
||||
data: branches,
|
||||
isPending: branchesIsPending,
|
||||
isError: branchesIsError,
|
||||
} = useAllProjectsBranchesQuery({ enabled: isLoggedIn })
|
||||
|
||||
const anyIsPending = organizationsIsPending || projectsIsPending || branchesIsPending
|
||||
const anyIsError = organizationsIsError || projectsIsError || branchesIsError
|
||||
|
||||
const stateSummary: ProjectConfigVariablesState = isUserLoading
|
||||
? 'userLoading'
|
||||
: !isLoggedIn
|
||||
? 'loggedOut'
|
||||
: anyIsPending
|
||||
? 'loggedIn.dataPending'
|
||||
: anyIsError
|
||||
? 'loggedIn.dataError'
|
||||
: projects.length === 0
|
||||
? 'loggedIn.hasNoData'
|
||||
: 'loggedIn.hasData'
|
||||
|
||||
useOnLogout(clearSharedStoreData)
|
||||
|
||||
const formattedData: InstanceData[] = useMemo(
|
||||
() =>
|
||||
stateSummary !== 'loggedIn.hasData'
|
||||
? []
|
||||
: projects.flatMap((project) => {
|
||||
const organization =
|
||||
organizations.find((organization) => organization.id === project.organization_id) ??
|
||||
null
|
||||
|
||||
// @ts-ignore -- problem with OpenAPI spec that codegen reads from
|
||||
if (!project.is_branch_enabled) {
|
||||
return { organization, project }
|
||||
}
|
||||
|
||||
// @ts-ignore -- problem with OpenAPI spec that codegen reads from
|
||||
const projectBranches = branches[project.ref]
|
||||
if (!projectBranches) {
|
||||
return { organization, project }
|
||||
}
|
||||
|
||||
return projectBranches.map((branch) => ({ organization, project, branch }))
|
||||
}),
|
||||
[branches, organizations, projects, stateSummary]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedInstanceKey && typeof window !== undefined) {
|
||||
let storedInstanceKey: InstanceKey = null
|
||||
const storedMaybeInstanceKey = retrieve('local', LOCAL_STORAGE_KEYS.SAVED_ORG_PROJECT_BRANCH)
|
||||
if (storedMaybeInstanceKey) {
|
||||
storedInstanceKey = fromStoredString(storedMaybeInstanceKey)
|
||||
}
|
||||
setSelectedInstanceKey(storedInstanceKey ?? toInstanceKey(formattedData[0]) ?? null)
|
||||
}
|
||||
}, [selectedInstanceKey, setSelectedInstanceKey, formattedData])
|
||||
|
||||
return (
|
||||
<ProjectConfigVariablesView
|
||||
variable={variable}
|
||||
instances={formattedData}
|
||||
parentStateSummary={stateSummary}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import StorageManagement from './MDX/storage_management.mdx'
|
||||
import MigrationWarnings from './MDX/migration_warnings.mdx'
|
||||
import { CH } from '@code-hike/mdx/components'
|
||||
import RefHeaderSection from './reference/RefHeaderSection'
|
||||
import { ProjectConfigVariables } from './ProjectConfigVariables'
|
||||
|
||||
// Ref version specific
|
||||
import CliGlobalFlagsHandler from '~/components/reference/enrichments/cli/CliGlobalFlagsHandler'
|
||||
@@ -64,7 +63,6 @@ const components = {
|
||||
SocialProviderSetup,
|
||||
SocialProviderSettingsSupabase,
|
||||
StepHikeCompact,
|
||||
ProjectConfigVariables,
|
||||
StorageManagement,
|
||||
Alert: (props: any) => (
|
||||
<Alert {...props} className="not-prose">
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
export const IS_PLATFORM = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true'
|
||||
export const LOCAL_SUPABASE = process.env.NEXT_PUBLIC_LOCAL_SUPABASE === 'true'
|
||||
export const API_URL = (
|
||||
process.env.NODE_ENV === 'development'
|
||||
? process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080'
|
||||
: process.env.NEXT_PUBLIC_API_URL
|
||||
).replace(/\/platform$/, '')
|
||||
export const API_URL = process.env.NEXT_PUBLIC_API_URL
|
||||
@@ -1,87 +0,0 @@
|
||||
import { ResponseError } from '~/types/fetch'
|
||||
import { get } from './fetchWrappers'
|
||||
import { UseQueryOptions, useQuery } from '@tanstack/react-query'
|
||||
import { getProjects } from './projects'
|
||||
|
||||
const branchKeys = {
|
||||
list: (projectRef: string | undefined) => ['projects', projectRef, 'branches'] as const,
|
||||
listAll: () => ['all-projects', 'all-branches'] as const,
|
||||
}
|
||||
|
||||
export interface BranchVariables {
|
||||
projectRef?: string
|
||||
}
|
||||
|
||||
async function getBranches({ projectRef }: BranchVariables, signal?: AbortSignal) {
|
||||
if (!projectRef) throw Error('projectRef is required')
|
||||
|
||||
const { data, error } = await get('/v1/projects/{ref}/branches', {
|
||||
params: {
|
||||
path: { ref: projectRef },
|
||||
},
|
||||
signal,
|
||||
})
|
||||
if (error) throw error
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
export type BranchesData = Awaited<ReturnType<typeof getBranches>>
|
||||
type BranchesError = ResponseError
|
||||
|
||||
export function useBranchesQuery<TData = BranchesData>(
|
||||
{ projectRef }: BranchVariables,
|
||||
{ enabled = true, ...options }: UseQueryOptions<BranchesData, BranchesError, TData>
|
||||
) {
|
||||
return useQuery<BranchesData, BranchesError, TData>({
|
||||
queryKey: branchKeys.list(projectRef),
|
||||
queryFn: ({ signal }) => getBranches({ projectRef }, signal),
|
||||
enabled,
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
async function getAllBranches(signal?: AbortSignal) {
|
||||
const allProjects = await getProjects(signal)
|
||||
|
||||
const branchesList = await Promise.all(
|
||||
allProjects.map(async (project) => {
|
||||
// @ts-ignore -- problem with OpenAPI spec that codegen reads from
|
||||
if (!project.is_branch_enabled) {
|
||||
return null
|
||||
}
|
||||
|
||||
// @ts-ignore -- problem with OpenAPI spec that codegen reads from
|
||||
const projectRef = project.ref as string
|
||||
try {
|
||||
const branches = await getBranches({ projectRef })
|
||||
return { [projectRef]: branches }
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const branchesMap = branchesList.reduce((record, branch) => {
|
||||
if (!branch) {
|
||||
return record
|
||||
}
|
||||
return Object.assign(record, branch)
|
||||
}, {})
|
||||
return branchesMap
|
||||
}
|
||||
|
||||
export type AllBranchesData = Awaited<ReturnType<typeof getAllBranches>>
|
||||
type AllBranchesError = ResponseError
|
||||
|
||||
export function useAllProjectsBranchesQuery<TData = AllBranchesData>({
|
||||
enabled = true,
|
||||
...options
|
||||
}: Omit<UseQueryOptions<AllBranchesData, AllBranchesError, TData>, 'queryKey'> = {}) {
|
||||
return useQuery<AllBranchesData, AllBranchesError, TData>({
|
||||
queryKey: branchKeys.listAll(),
|
||||
queryFn: ({ signal }) => getAllBranches(signal),
|
||||
enabled,
|
||||
...options,
|
||||
})
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import { type paths } from '~/types/api'
|
||||
import createClient from 'openapi-fetch'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { API_URL } from '../constants'
|
||||
import { getAccessToken } from '../userAuth'
|
||||
|
||||
const DEFAULT_HEADERS = {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
}
|
||||
|
||||
const { GET: _get, POST: _post } = createClient<paths>({
|
||||
baseUrl: API_URL,
|
||||
referrerPolicy: 'no-referrer-when-downgrade',
|
||||
headers: DEFAULT_HEADERS,
|
||||
})
|
||||
|
||||
export async function constructHeaders(
|
||||
headersInit?: HeadersInit | undefined,
|
||||
{ allowUnauthenticated = false }: { allowUnauthenticated?: boolean } = {}
|
||||
) {
|
||||
const requestId = uuidv4()
|
||||
const headers = new Headers(headersInit)
|
||||
|
||||
headers.set('X-Request-Id', requestId)
|
||||
|
||||
if (!headers.has('Authorization')) {
|
||||
const accessToken = await getAccessToken()
|
||||
if (accessToken) {
|
||||
headers.set('Authorization', `Bearer ${accessToken}`)
|
||||
} else if (!allowUnauthenticated) {
|
||||
throw Error("can't fetch authenticated routes without signing in")
|
||||
}
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
export const get: typeof _get = async (url, init) => {
|
||||
let headers: Headers = null
|
||||
try {
|
||||
headers = await constructHeaders(init?.headers)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return { error: err, response: undefined }
|
||||
}
|
||||
|
||||
return await _get(url, {
|
||||
...init,
|
||||
headers,
|
||||
})
|
||||
}
|
||||
|
||||
export const post: typeof _post = async (url, init) => {
|
||||
let headers: Headers = null
|
||||
try {
|
||||
headers = await constructHeaders(init?.headers)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return { error: err, response: undefined }
|
||||
}
|
||||
|
||||
return await _post(url, {
|
||||
...init,
|
||||
headers,
|
||||
})
|
||||
}
|
||||
|
||||
export const unauthedAllowedPost: typeof _post = async (url, init) => {
|
||||
let headers: Headers = null
|
||||
try {
|
||||
headers = await constructHeaders(init?.headers, { allowUnauthenticated: true })
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return { error: err, response: undefined }
|
||||
}
|
||||
|
||||
return await _post(url, {
|
||||
...init,
|
||||
headers,
|
||||
})
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { get } from './fetchWrappers'
|
||||
import { ResponseError } from '~/types/fetch'
|
||||
|
||||
const organizationKeys = {
|
||||
list: () => ['organizations'] as const,
|
||||
}
|
||||
|
||||
async function getOrganizations(signal?: AbortSignal) {
|
||||
const { data, error } = await get('/platform/organizations', { signal })
|
||||
if (error) throw error
|
||||
return data
|
||||
}
|
||||
|
||||
export type OrganizationsData = Awaited<ReturnType<typeof getOrganizations>>
|
||||
type OrganizationsError = ResponseError
|
||||
|
||||
export function useOrganizationsQuery<TData = OrganizationsData>({
|
||||
enabled = true,
|
||||
...options
|
||||
}: Omit<UseQueryOptions<OrganizationsData, OrganizationsError, TData>, 'queryKey'> = {}) {
|
||||
return useQuery<OrganizationsData, OrganizationsError, TData>({
|
||||
queryKey: organizationKeys.list(),
|
||||
queryFn: ({ signal }) => getOrganizations(signal),
|
||||
enabled,
|
||||
...options,
|
||||
})
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { ResponseError } from '~/types/fetch'
|
||||
import { get } from './fetchWrappers'
|
||||
import { UseQueryOptions, useQuery } from '@tanstack/react-query'
|
||||
|
||||
const projectApiKeys = {
|
||||
api: (projectRef: string | undefined) => ['projects', projectRef, 'api'] as const,
|
||||
}
|
||||
|
||||
export interface ProjectApiVariables {
|
||||
projectRef?: string
|
||||
}
|
||||
|
||||
async function getProjectApi({ projectRef }: ProjectApiVariables, signal?: AbortSignal) {
|
||||
if (!projectRef) {
|
||||
throw Error('projectRef is required')
|
||||
}
|
||||
|
||||
const { data, error } = await get('/platform/props/project/{ref}/api', {
|
||||
params: {
|
||||
path: { ref: projectRef },
|
||||
},
|
||||
signal,
|
||||
})
|
||||
if (error) throw error
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
type ProjectApiData = Awaited<ReturnType<typeof getProjectApi>>
|
||||
type ProjectApiError = ResponseError
|
||||
|
||||
export function useProjectApiQuery<TData = ProjectApiData>(
|
||||
{ projectRef }: ProjectApiVariables,
|
||||
{
|
||||
enabled = true,
|
||||
...options
|
||||
}: Omit<UseQueryOptions<ProjectApiData, ProjectApiError, TData>, 'queryKey'> = {}
|
||||
) {
|
||||
return useQuery<ProjectApiData, ProjectApiError, TData>({
|
||||
queryKey: projectApiKeys.api(projectRef),
|
||||
queryFn: ({ signal }) => getProjectApi({ projectRef }, signal),
|
||||
enabled,
|
||||
...options,
|
||||
})
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { ResponseError } from '~/types/fetch'
|
||||
import { get } from './fetchWrappers'
|
||||
|
||||
const projectKeys = {
|
||||
list: () => ['all-projects'] as const,
|
||||
}
|
||||
|
||||
export async function getProjects(signal?: AbortSignal) {
|
||||
const { data, error } = await get('/platform/projects', { signal })
|
||||
if (error) throw error
|
||||
return data
|
||||
}
|
||||
|
||||
export type ProjectsData = Awaited<ReturnType<typeof getProjects>>
|
||||
type ProjectsError = ResponseError
|
||||
|
||||
export function useProjectsQuery<TData = ProjectsData>({
|
||||
enabled = true,
|
||||
...options
|
||||
}: Omit<UseQueryOptions<ProjectsData, ProjectsError, TData>, 'queryKey'> = {}) {
|
||||
return useQuery<ProjectsData, ProjectsError, TData>({
|
||||
queryKey: projectKeys.list(),
|
||||
queryFn: ({ signal }) => getProjects(signal),
|
||||
enabled,
|
||||
...options,
|
||||
})
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { QueryClient } from '@tanstack/react-query'
|
||||
import { useState } from 'react'
|
||||
|
||||
let queryClient: QueryClient | undefined
|
||||
|
||||
function getQueryClient() {
|
||||
const _queryClient =
|
||||
queryClient ??
|
||||
new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: (failureCount, error) => {
|
||||
// Don't retry on 404s
|
||||
if (
|
||||
typeof error === 'object' &&
|
||||
error !== null &&
|
||||
'code' in error &&
|
||||
error.code === 404
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (failureCount < 3) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// For SSG and SSR always create a new queryClient
|
||||
if (typeof window === 'undefined') return _queryClient
|
||||
|
||||
// Create the query client once in the client
|
||||
if (!queryClient) queryClient = _queryClient
|
||||
return queryClient
|
||||
}
|
||||
|
||||
export function useRootQueryClient() {
|
||||
const [_queryClient] = useState(() => getQueryClient())
|
||||
|
||||
return _queryClient
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
interface DataProps {
|
||||
[prop: string]: any
|
||||
}
|
||||
|
||||
export const post = (url: string, data: DataProps, options = {}) => {
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
referrerPolicy: 'no-referrer-when-downgrade',
|
||||
body: JSON.stringify(data),
|
||||
...options,
|
||||
}).catch((error) => {
|
||||
console.error('Error at fetchWrapper - post:', error)
|
||||
})
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
export const LOCAL_STORAGE_KEYS = {
|
||||
SAVED_ORG_PROJECT_BRANCH: 'docs.ui.user.selected.org_project_branch',
|
||||
} as const
|
||||
|
||||
type LocalStorageKey = (typeof LOCAL_STORAGE_KEYS)[keyof typeof LOCAL_STORAGE_KEYS]
|
||||
|
||||
type StorageType = 'local' | 'session'
|
||||
|
||||
function getStorage(storageType: StorageType) {
|
||||
return storageType === 'local' ? window.localStorage : window.sessionStorage
|
||||
}
|
||||
|
||||
export function store(storageType: StorageType, key: LocalStorageKey, value: string) {
|
||||
if (typeof window === 'undefined') return
|
||||
const storage = getStorage(storageType)
|
||||
|
||||
try {
|
||||
storage.setItem(key, value)
|
||||
} catch {
|
||||
console.error(`Failed to set storage item with key "${key}"`)
|
||||
}
|
||||
}
|
||||
|
||||
export function retrieve(storageType: StorageType, key: LocalStorageKey) {
|
||||
if (typeof window === 'undefined') return
|
||||
const storage = getStorage(storageType)
|
||||
return storage.getItem(key)
|
||||
}
|
||||
|
||||
export function remove(storageType: StorageType, key: LocalStorageKey) {
|
||||
if (typeof window === 'undefined') return
|
||||
const storage = getStorage(storageType)
|
||||
return storage.removeItem(key)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { gotrueClient } from 'common'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const auth = gotrueClient
|
||||
|
||||
export async function getAccessToken() {
|
||||
// ignore if server-side
|
||||
if (typeof window === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
const {
|
||||
data: { session },
|
||||
error,
|
||||
} = await auth.getSession()
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
return session?.access_token
|
||||
}
|
||||
|
||||
export function useOnLogout(callback: () => void) {
|
||||
useEffect(() => {
|
||||
const {
|
||||
data: {
|
||||
subscription: { unsubscribe },
|
||||
},
|
||||
} = auth.onAuthStateChange((event) => {
|
||||
if (event === 'SIGNED_OUT') {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
|
||||
return unsubscribe
|
||||
}, [callback])
|
||||
}
|
||||
@@ -7,7 +7,6 @@
|
||||
"dev:secrets:pull": "AWS_PROFILE=supabase-dev node ../../scripts/getSecrets.js -n local/docs",
|
||||
"build": "next build",
|
||||
"build:analyze": "ANALYZE=true next build",
|
||||
"codegen": "openapi-typescript http://localhost:8080/api-json -o types/api.d.ts && prettier --write types/api.d.ts",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"typecheck": "tsc --noEmit",
|
||||
@@ -19,7 +18,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@code-hike/mdx": "^0.9.0",
|
||||
"@hcaptcha/react-hcaptcha": "^1.9.2",
|
||||
"@mdx-js/loader": "^2.1.5",
|
||||
"@mdx-js/react": "^2.3.0",
|
||||
"@next/bundle-analyzer": "^13.5.6",
|
||||
@@ -31,7 +29,6 @@
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@supabase/auth-helpers-react": "^0.4.2",
|
||||
"@supabase/supabase-js": "^2.38.2",
|
||||
"@tanstack/react-query": "^5.13.4",
|
||||
"common": "*",
|
||||
"config": "*",
|
||||
"framer-motion": "^6.5.1",
|
||||
@@ -56,7 +53,6 @@
|
||||
"next-seo": "^5.14.1",
|
||||
"next-themes": "^0.2.1",
|
||||
"openai": "^4.17.0",
|
||||
"openapi-fetch": "^0.8.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-intersection-observer": "^9.5.2",
|
||||
@@ -86,7 +82,6 @@
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"openapi-types": "^12.0.2",
|
||||
"openapi-typescript": "^6.7.2",
|
||||
"sass": "^1.68.0",
|
||||
"tsconfig": "*",
|
||||
"tsx": "^4.6.2",
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
import { Button_Shadcn_ } from 'ui'
|
||||
import { auth } from '~/lib/userAuth'
|
||||
|
||||
export function getServerSideProps() {
|
||||
if (process.env.NEXT_PUBLIC_DEV_AUTH_PAGE === 'true') {
|
||||
return {
|
||||
props: {},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
notFound: true,
|
||||
}
|
||||
}
|
||||
|
||||
export default function DevOnlySecretAuth() {
|
||||
const router = useRouter()
|
||||
const [email, setEmail] = useState('')
|
||||
|
||||
function signInWithGitHub() {
|
||||
auth.signInWithOAuth({
|
||||
provider: 'github',
|
||||
options: {
|
||||
redirectTo: process.env.NEXT_PUBLIC_SITE_URL,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function signOut() {
|
||||
auth.signOut().then(({ error }) => {
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
router.push('/')
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-10 flex items-center justify-center">
|
||||
<section className="space-y-4">
|
||||
<h1>Sign in</h1>
|
||||
<p>
|
||||
This is a dev-only route to sign in and test authenticated actions within docs. In staging
|
||||
and production, signin is managed via dashboard because docs and dashboard are proxied to
|
||||
the same domain.
|
||||
</p>
|
||||
<form className="flex flex-col gap-2 max-w-sm">
|
||||
<Button_Shadcn_ type="button" onClick={() => signInWithGitHub()}>
|
||||
{' '}
|
||||
Sign in with GitHub
|
||||
</Button_Shadcn_>
|
||||
<Button_Shadcn_ type="button" onClick={signOut}>
|
||||
Sign out
|
||||
</Button_Shadcn_>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+32
-69
@@ -6,49 +6,21 @@ import '../styles/prism-okaidia.scss'
|
||||
|
||||
import { SessionContextProvider } from '@supabase/auth-helpers-react'
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { QueryClientProvider, useQueryClient } from '@tanstack/react-query'
|
||||
import { AuthProvider, ThemeProvider, useTelemetryProps, useThemeSandbox } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { PropsWithChildren, useCallback, useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { AppPropsWithLayout } from 'types'
|
||||
import { CommandMenuProvider, PortalToast, useConsent } from 'ui'
|
||||
import { TabsProvider } from 'ui/src/components/Tabs'
|
||||
import Favicons from '~/components/Favicons'
|
||||
import SiteLayout from '~/layouts/SiteLayout'
|
||||
import { IS_PLATFORM } from '~/lib/constants'
|
||||
import { unauthedAllowedPost } from '~/lib/fetch/fetchWrappers'
|
||||
import { useRootQueryClient } from '~/lib/fetch/queryClient'
|
||||
import { useOnLogout } from '~/lib/userAuth'
|
||||
import { LOCAL_STORAGE_KEYS, remove } from '~/lib/storage'
|
||||
|
||||
/**
|
||||
*
|
||||
* !!! IMPORTANT !!!
|
||||
* Ensure data is cleared on sign out.
|
||||
*
|
||||
* **/
|
||||
function SignOutHandler({ children }: PropsWithChildren) {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const cleanUp = useCallback(() => {
|
||||
queryClient.cancelQueries()
|
||||
queryClient.clear()
|
||||
|
||||
Object.keys(LOCAL_STORAGE_KEYS).forEach((key) => {
|
||||
remove('local', LOCAL_STORAGE_KEYS[key])
|
||||
})
|
||||
}, [queryClient])
|
||||
|
||||
useOnLogout(cleanUp)
|
||||
|
||||
return <>{children}</>
|
||||
}
|
||||
import { API_URL, IS_PLATFORM } from '~/lib/constants'
|
||||
import { post } from '~/lib/fetchWrappers'
|
||||
|
||||
function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
const router = useRouter()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const { consentValue, hasAcceptedConsent } = useConsent()
|
||||
const queryClient = useRootQueryClient()
|
||||
|
||||
useThemeSandbox()
|
||||
|
||||
@@ -63,22 +35,15 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
|
||||
const handlePageTelemetry = useCallback(
|
||||
(route: string) => {
|
||||
if (IS_PLATFORM) {
|
||||
unauthedAllowedPost('/platform/telemetry/page', {
|
||||
body: {
|
||||
referrer: document.referrer,
|
||||
title: document.title,
|
||||
route,
|
||||
ga: {
|
||||
screen_resolution: telemetryProps?.screenResolution,
|
||||
language: telemetryProps?.language,
|
||||
session_id: '',
|
||||
},
|
||||
},
|
||||
}).catch((e) => {
|
||||
console.error('Problem sending telemetry:', e)
|
||||
})
|
||||
}
|
||||
return post(`${API_URL}/telemetry/page`, {
|
||||
referrer: document.referrer,
|
||||
title: document.title,
|
||||
route,
|
||||
ga: {
|
||||
screen_resolution: telemetryProps?.screenResolution,
|
||||
language: telemetryProps?.language,
|
||||
},
|
||||
})
|
||||
},
|
||||
[telemetryProps]
|
||||
)
|
||||
@@ -108,7 +73,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
return () => {
|
||||
router.events.off('routeChangeComplete', handleRouteChange)
|
||||
}
|
||||
}, [router, handlePageTelemetry, consentValue, hasAcceptedConsent])
|
||||
}, [router, handlePageTelemetry, consentValue])
|
||||
|
||||
/**
|
||||
* Save/restore scroll position when reloading or navigating back/forward.
|
||||
@@ -155,7 +120,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
if (router.isReady) {
|
||||
handlePageTelemetry(router.basePath + router.asPath)
|
||||
}
|
||||
}, [router, handlePageTelemetry, consentValue, hasAcceptedConsent])
|
||||
}, [router, handlePageTelemetry, consentValue])
|
||||
|
||||
/**
|
||||
* Reference docs use `history.pushState()` to jump to
|
||||
@@ -176,35 +141,33 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
}
|
||||
}, [router])
|
||||
|
||||
const AuthContainer = ({ children }) => {
|
||||
const SITE_TITLE = 'Supabase Documentation'
|
||||
|
||||
const AuthContainer = (props) => {
|
||||
return IS_PLATFORM ? (
|
||||
<SessionContextProvider supabaseClient={supabase}>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
<AuthProvider>{props.children}</AuthProvider>
|
||||
</SessionContextProvider>
|
||||
) : (
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
<AuthProvider>{props.children}</AuthProvider>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Favicons />
|
||||
<AuthContainer>
|
||||
<SignOutHandler>
|
||||
<ThemeProvider defaultTheme="system" enableSystem disableTransitionOnChange>
|
||||
<CommandMenuProvider site="docs">
|
||||
<TabsProvider>
|
||||
<SiteLayout>
|
||||
<PortalToast />
|
||||
<Component {...pageProps} />
|
||||
</SiteLayout>
|
||||
</TabsProvider>
|
||||
</CommandMenuProvider>
|
||||
</ThemeProvider>
|
||||
</SignOutHandler>
|
||||
</AuthContainer>
|
||||
</QueryClientProvider>
|
||||
<Favicons />
|
||||
<AuthContainer>
|
||||
<ThemeProvider defaultTheme="system" enableSystem disableTransitionOnChange>
|
||||
<CommandMenuProvider site="docs">
|
||||
<TabsProvider>
|
||||
<SiteLayout>
|
||||
<PortalToast />
|
||||
<Component {...pageProps} />
|
||||
</SiteLayout>
|
||||
</TabsProvider>
|
||||
</CommandMenuProvider>
|
||||
</ThemeProvider>
|
||||
</AuthContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -79,10 +79,7 @@ export const meta = {
|
||||
|
||||
<StepHikeCompact.Details title="Initialize the Supabase client">
|
||||
|
||||
Open `lib/main.dart` and edit the main function to initialize Supabase using your project URL and public API (anon) key:
|
||||
|
||||
<ProjectConfigVariables variable="url" />
|
||||
<ProjectConfigVariables variable="anonKey" />
|
||||
Open `lib/main.dart` and edit the main function to initialize Supabase using your [project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
|
||||
@@ -100,11 +100,7 @@ export const meta = {
|
||||
|
||||
For the sake of simplicity, we will create a client in the `MainActivity.kt` file at the top just below the imports.
|
||||
|
||||
Replace the `supabaseUrl` and `supabaseKey` with your own:
|
||||
|
||||
<ProjectConfigVariables variable="url" />
|
||||
<ProjectConfigVariables variable="anonKey" />
|
||||
|
||||
Replace the `supabaseUrl` and `supabaseKey` with your own found in [your dashboard](https://supabase.com/dashboard/project/_/settings/api).
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
@@ -66,18 +66,15 @@ export const meta = {
|
||||
<StepHikeCompact.Step step={3}>
|
||||
<StepHikeCompact.Details title="Declare Supabase Environment Variables">
|
||||
|
||||
Rename `.env.local.example` to `.env.local` and populate with your Supabase connection variables:
|
||||
|
||||
<ProjectConfigVariables variable="url" />
|
||||
<ProjectConfigVariables variable="anonKey" />
|
||||
Rename `.env.local.example` to `.env.local` and populate with [your project's URL and Anon Key](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```text .env.local
|
||||
NEXT_PUBLIC_SUPABASE_URL=<SUBSTITUTE_SUPABASE_URL>
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=<SUBSTITUTE_SUPABASE_ANON_KEY>
|
||||
NEXT_PUBLIC_SUPABASE_URL=your-project-url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
@@ -78,10 +78,7 @@ export const meta = {
|
||||
<StepHikeCompact.Step step={4}>
|
||||
<StepHikeCompact.Details title="Query data from the app">
|
||||
|
||||
In `src/App.vue`, create a Supabase client using your project URL and public API (anon) key:
|
||||
|
||||
<ProjectConfigVariables variable="url" />
|
||||
<ProjectConfigVariables variable="anonKey" />
|
||||
In `src/App.vue`, create a Supabase client using your [Project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
Replace the existing content in your `src/App.vue` file with the following code.
|
||||
|
||||
|
||||
@@ -77,10 +77,7 @@ export const meta = {
|
||||
<StepHikeCompact.Step step={4}>
|
||||
<StepHikeCompact.Details title="Query data from the app">
|
||||
|
||||
In `App.jsx`, create a Supabase client using your project URL and public API (anon) key:
|
||||
|
||||
<ProjectConfigVariables variable="url" />
|
||||
<ProjectConfigVariables variable="anonKey" />
|
||||
In `App.jsx`, create a Supabase client using your [Project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
Add a `getCountries` function to fetch the data and display the query result to the page.
|
||||
|
||||
|
||||
@@ -101,9 +101,6 @@ export const meta = {
|
||||
|
||||
You now have to update the `supabaseClient` with the `SUPABASE_URL` and `SUPABASE_KEY` of your Supabase API. The `supabaseClient` is used in auth provider and data provider methods that allow the refine app to connect to your Supabase backend.
|
||||
|
||||
<ProjectConfigVariables variable="url" />
|
||||
<ProjectConfigVariables variable="anonKey" />
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
@@ -77,10 +77,7 @@ export const meta = {
|
||||
<StepHikeCompact.Step step={4}>
|
||||
<StepHikeCompact.Details title="Query data from the app">
|
||||
|
||||
In `App.jsx`, create a Supabase client using your project URL and public API (anon) key:
|
||||
|
||||
<ProjectConfigVariables variable="url" />
|
||||
<ProjectConfigVariables variable="anonKey" />
|
||||
In `App.jsx`, create a Supabase client using your [Project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
Add a `getCountries` function to fetch the data and display the query result to the page.
|
||||
|
||||
|
||||
@@ -77,10 +77,7 @@ export const meta = {
|
||||
<StepHikeCompact.Step step={4}>
|
||||
<StepHikeCompact.Details title="Create the Supabase client">
|
||||
|
||||
Create a `/src/lib` directory in your SvelteKit app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your project URL and public API (anon) key:
|
||||
|
||||
<ProjectConfigVariables variable="url" />
|
||||
<ProjectConfigVariables variable="anonKey" />
|
||||
Create a `/src/lib` directory in your SvelteKit app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
|
||||
@@ -77,10 +77,7 @@ export const meta = {
|
||||
<StepHikeCompact.Step step={4}>
|
||||
<StepHikeCompact.Details title="Create the Supabase client">
|
||||
|
||||
Create a `/src/lib` directory in your Vue app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your project URL and public API (anon) key:
|
||||
|
||||
<ProjectConfigVariables variable="url" />
|
||||
<ProjectConfigVariables variable="anonKey" />
|
||||
Create a `/src/lib` directory in your Vue app, create a file called `supabaseClient.js` and add the following code to initialize the Supabase client with your [project URL and public API (anon) key](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
|
||||
Vendored
-12587
File diff suppressed because it is too large.
Load diff
@@ -1,5 +0,0 @@
|
||||
export interface ResponseError {
|
||||
code?: number
|
||||
message: string
|
||||
requestId?: string
|
||||
}
|
||||
Generated
+6
-54
@@ -486,7 +486,6 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@code-hike/mdx": "^0.9.0",
|
||||
"@hcaptcha/react-hcaptcha": "^1.9.2",
|
||||
"@mdx-js/loader": "^2.1.5",
|
||||
"@mdx-js/react": "^2.3.0",
|
||||
"@next/bundle-analyzer": "^13.5.6",
|
||||
@@ -498,7 +497,6 @@
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@supabase/auth-helpers-react": "^0.4.2",
|
||||
"@supabase/supabase-js": "^2.38.2",
|
||||
"@tanstack/react-query": "^5.13.4",
|
||||
"common": "*",
|
||||
"config": "*",
|
||||
"framer-motion": "^6.5.1",
|
||||
@@ -523,7 +521,6 @@
|
||||
"next-seo": "^5.14.1",
|
||||
"next-themes": "^0.2.1",
|
||||
"openai": "^4.17.0",
|
||||
"openapi-fetch": "^0.8.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-intersection-observer": "^9.5.2",
|
||||
@@ -553,37 +550,12 @@
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"openapi-types": "^12.0.2",
|
||||
"openapi-typescript": "^6.7.2",
|
||||
"sass": "^1.68.0",
|
||||
"tsconfig": "*",
|
||||
"tsx": "^4.6.2",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
},
|
||||
"apps/docs/node_modules/@tanstack/query-core": {
|
||||
"version": "5.13.4",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.13.4.tgz",
|
||||
"integrity": "sha512-8+rJucXvC/xlr4OrxHhEIob/cTlbT4fgmz1VsvB0D12FRStKaXeLORNGcOhSAynRd2NL74SV/Qq0IIb4DedLcA==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"apps/docs/node_modules/@tanstack/react-query": {
|
||||
"version": "5.13.4",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.13.4.tgz",
|
||||
"integrity": "sha512-3HjvkFFriEQwffUXtKHPiwkfFXUGbs46YATTzzyK1+Pw6Ekd3kwzS50e45qdamWuEXmXxyo5S1zp534LdFG0Rw==",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.13.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"apps/docs/node_modules/@types/mdast": {
|
||||
"version": "3.0.13",
|
||||
"license": "MIT",
|
||||
@@ -676,14 +648,6 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"apps/docs/node_modules/openapi-fetch": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/openapi-fetch/-/openapi-fetch-0.8.2.tgz",
|
||||
"integrity": "sha512-4g+NLK8FmQ51RW6zLcCBOVy/lwYmFJiiT+ckYZxJWxUxH4XFhsNcX2eeqVMfVOi+mDNFja6qDXIZAz2c5J/RVw==",
|
||||
"dependencies": {
|
||||
"openapi-typescript-helpers": "^0.0.5"
|
||||
}
|
||||
},
|
||||
"apps/docs/node_modules/puppeteer": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-16.2.0.tgz",
|
||||
@@ -4652,18 +4616,11 @@
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz",
|
||||
"integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA=="
|
||||
},
|
||||
"node_modules/@hcaptcha/loader": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@hcaptcha/loader/-/loader-1.1.2.tgz",
|
||||
"integrity": "sha512-YEBXCGNUBpPJUd5UZg64jBU6YMOhDjGdzS6dUCI9P8uIJttuQz3P76wVmK9kdKOuvLUaB7HwVBBmNv/bkAaIEw=="
|
||||
},
|
||||
"node_modules/@hcaptcha/react-hcaptcha": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@hcaptcha/react-hcaptcha/-/react-hcaptcha-1.9.2.tgz",
|
||||
"integrity": "sha512-FKsfxi2PV0UTFp3TvW8L02z2MHZjhBVOvH1OznR4ccxjugCZa8ix4weP/wKRUaLHcKwmCfXSu35Agen41aP1mQ==",
|
||||
"version": "1.8.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.17.9",
|
||||
"@hcaptcha/loader": "^1.0.10"
|
||||
"@babel/runtime": "^7.17.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.3.0",
|
||||
@@ -28164,9 +28121,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/openapi-typescript": {
|
||||
"version": "6.7.2",
|
||||
"resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-6.7.2.tgz",
|
||||
"integrity": "sha512-7rsUArlMBqmSaRd6EzPl2nGKzPFNRicsRGrxf6W+/HLEDZoOxghR3B53YlyGjcqak8YDZMBNzZQ3o93Bp3qY9Q==",
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-6.7.1.tgz",
|
||||
"integrity": "sha512-Q3Ltt0KUm2smcPrsaR8qKmSwQ1KM4yGDJVoQdpYa0yvKPeN8huDx5utMT7DvwvJastHHzUxajjivK3WN2+fobg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-colors": "^4.1.3",
|
||||
@@ -28180,11 +28137,6 @@
|
||||
"openapi-typescript": "bin/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/openapi-typescript-helpers": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/openapi-typescript-helpers/-/openapi-typescript-helpers-0.0.5.tgz",
|
||||
"integrity": "sha512-MRffg93t0hgGZbYTxg60hkRIK2sRuEOHEtCUgMuLgbCC33TMQ68AmxskzUlauzZYD47+ENeGV/ElI7qnWqrAxA=="
|
||||
},
|
||||
"node_modules/openapi-typescript/node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"dev": true,
|
||||
|
||||
@@ -159,8 +159,6 @@ export {
|
||||
|
||||
export { Input as Input_Shadcn_ } from './src/components/shadcn/ui/input'
|
||||
|
||||
export { Button as Button_Shadcn_ } from './src/components/shadcn/ui/button'
|
||||
|
||||
export { TextArea as TextArea_Shadcn_ } from './src/components/shadcn/ui/text-area'
|
||||
|
||||
export { Label as Label_Shadcn_ } from './src/components/shadcn/ui/label'
|
||||
|
||||
Reference in new issue
Block a user