No files matched your search
@@ -8,6 +8,7 @@
|
||||
- [Running turborepo](#running-turborepo)
|
||||
- [Shared components](#shared-components)
|
||||
- [Installing packages](#installing-packages)
|
||||
- [New Supabase docs](#new-supabase-docs)
|
||||
3. [Create a pull request](#create-a-pull-request)
|
||||
|
||||
- [Common tasks](#common-tasks)
|
||||
@@ -106,6 +107,14 @@ For example:
|
||||
|
||||
You do not need to install `devDependencies` in each workspace. These can all be installed in the root package.
|
||||
|
||||
#### New Supabase docs
|
||||
|
||||
Following the changes to the [Supabase docs](https://supabase.com/blog/new-supabase-docs-built-with-nextjs) the following is needed to run the new docs locally:
|
||||
|
||||
- Inside of `apps/docs` create a `.env.local` file with the following: `NEXT_PUBLIC_NEW_DOCS=true`
|
||||
|
||||
Now when you run a local devlopment docs server you will see the new docs site.
|
||||
|
||||
---
|
||||
|
||||
## Create a pull request
|
||||
|
||||
@@ -248,6 +248,7 @@ export const menuItems: NavMenu = {
|
||||
{ name: 'Quickstart', url: '/guides/storage/quickstart', items: [] },
|
||||
{ name: 'Access Control', url: '/guides/storage/access-control', items: [] },
|
||||
{ name: 'CDN', url: '/guides/storage/cdn', items: [] },
|
||||
{ name: 'Image Transformations', url: '/guides/storage/image-transformations', items: [] },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useMenuLevelId } from '~/hooks/useMenuState'
|
||||
import { useTheme } from 'common/Providers'
|
||||
|
||||
const home = [
|
||||
[
|
||||
{
|
||||
label: 'Home',
|
||||
icon: '/img/icons/menu/home',
|
||||
href: '/',
|
||||
level: 'home',
|
||||
},
|
||||
{
|
||||
label: 'Getting Started',
|
||||
icon: '/img/icons/menu/getting-started',
|
||||
href: '/guides/getting-started',
|
||||
level: 'gettingstarted',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Database',
|
||||
icon: '/img/icons/menu/database',
|
||||
href: '/guides/database',
|
||||
level: 'database',
|
||||
},
|
||||
{
|
||||
label: 'Auth',
|
||||
icon: '/img/icons/menu/auth',
|
||||
href: '/guides/auth/overview',
|
||||
level: 'auth',
|
||||
},
|
||||
{
|
||||
label: 'Edge Functions',
|
||||
icon: '/img/icons/menu/functions',
|
||||
href: '/guides/functions',
|
||||
level: 'functions',
|
||||
},
|
||||
{
|
||||
label: 'Realtime',
|
||||
icon: '/img/icons/menu/realtime',
|
||||
href: '/guides/realtime',
|
||||
level: 'realtime',
|
||||
},
|
||||
{
|
||||
label: 'Storage',
|
||||
icon: '/img/icons/menu/storage',
|
||||
href: '/guides/storage',
|
||||
level: 'storage',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Platform',
|
||||
icon: '/img/icons/menu/platform',
|
||||
href: '/guides/platform',
|
||||
level: 'platform',
|
||||
},
|
||||
{
|
||||
label: 'Resources',
|
||||
icon: '/img/icons/menu/platform',
|
||||
href: '/guides/resources',
|
||||
level: 'resources',
|
||||
},
|
||||
{
|
||||
label: 'Integrations',
|
||||
icon: '/img/icons/menu/integrations',
|
||||
hasLightIcon: true,
|
||||
href: '/guides/integrations',
|
||||
level: 'integrations',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Client Library Reference',
|
||||
},
|
||||
{
|
||||
label: 'JavaScript',
|
||||
icon: '/img/icons/menu/reference-javascript',
|
||||
href: '/reference/javascript/introduction',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
{
|
||||
label: 'Flutter',
|
||||
icon: '/img/icons/menu/reference-dart',
|
||||
href: '/reference/dart/introduction',
|
||||
level: 'reference_dart',
|
||||
},
|
||||
{
|
||||
label: 'Tools Reference',
|
||||
},
|
||||
{
|
||||
label: 'Management API',
|
||||
icon: '/img/icons/menu/reference-api',
|
||||
href: '/reference/api/introduction',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
{
|
||||
label: 'Supabase CLI',
|
||||
icon: '/img/icons/menu/reference-cli',
|
||||
href: '/reference/cli/introduction',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
],
|
||||
]
|
||||
|
||||
const NavigationMenuHome = () => {
|
||||
const level = useMenuLevelId()
|
||||
const router = useRouter()
|
||||
const { isDarkMode } = useTheme()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
'transition-all duration-150 ease-out',
|
||||
level === 'home' || !level
|
||||
? 'opacity-100 ml-0 delay-150'
|
||||
: 'opacity-0 -ml-8 invisible absolute',
|
||||
].join(' ')}
|
||||
>
|
||||
<ul className="relative w-full flex flex-col gap-4">
|
||||
{home.map((section, sectionIndex) => {
|
||||
return (
|
||||
<>
|
||||
{sectionIndex !== 0 && (
|
||||
<div
|
||||
className="h-px w-full bg-blackA-300 dark:bg-whiteA-300"
|
||||
key={`section-${sectionIndex}-border`}
|
||||
></div>
|
||||
)}
|
||||
<div key={`section-${sectionIndex}`}>
|
||||
<div className="flex flex-col gap-3">
|
||||
{section.map((link) => {
|
||||
if (!link.href) {
|
||||
return (
|
||||
<span
|
||||
key={link.label}
|
||||
className="font-mono uppercase text-xs text-scale-900"
|
||||
>
|
||||
{link.label}
|
||||
</span>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Link href={link.href} passHref key={link.label}>
|
||||
<a>
|
||||
<li
|
||||
className={[
|
||||
'group flex items-center gap-3',
|
||||
'text-base transition-all duration-150 text-scale-1200 hover:text-brand-900 hover:cursor-pointer ',
|
||||
].join(' ')}
|
||||
>
|
||||
<Image
|
||||
alt={link.label}
|
||||
src={`${router.basePath}${
|
||||
isDarkMode ? link.icon : `${link.icon}-light`
|
||||
}${!link.icon.includes('png') ? '.svg' : ''}`}
|
||||
width={17}
|
||||
height={17}
|
||||
className="w-4 h-4 group-hover:scale-110 ease-out transition-all"
|
||||
/>
|
||||
{link.label}
|
||||
</li>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default NavigationMenuHome
|
||||
@@ -454,6 +454,7 @@ export const storage = {
|
||||
{ name: 'Quickstart', url: '/guides/storage/quickstart', items: [] },
|
||||
{ name: 'Access Control', url: '/guides/storage/access-control', items: [] },
|
||||
{ name: 'CDN', url: '/guides/storage/cdn', items: [] },
|
||||
{ name: 'Image Transformations', url: '/guides/storage/image-transformations', items: [] },
|
||||
],
|
||||
}
|
||||
|
||||
@@ -560,19 +561,19 @@ export const resources = {
|
||||
{
|
||||
name: 'Auth Server',
|
||||
url: '/reference/self-hosting-auth/introduction',
|
||||
icon: '/docs/img/icons/menu/reference-auth',
|
||||
icon: '/img/icons/menu/reference-auth',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'Storage Server',
|
||||
url: '/reference/self-hosting-storage/introduction',
|
||||
icon: '/docs/img/icons/menu/reference-storage',
|
||||
icon: '/img/icons/menu/reference-storage',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
name: 'Realtime Server',
|
||||
url: '/reference/self-hosting-realtime/introduction',
|
||||
icon: '/docs/img/icons/menu/reference-realtime',
|
||||
icon: '/img/icons/menu/reference-realtime',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
@@ -668,21 +669,21 @@ export const reference = {
|
||||
url: '/reference/javascript/start',
|
||||
level: 'reference_javascript',
|
||||
items: [],
|
||||
icon: '/img/icons/javascript.svg',
|
||||
icon: 'docs/img/icons/javascript.svg',
|
||||
},
|
||||
{
|
||||
name: 'supabase-dart',
|
||||
url: '/reference/dart/start',
|
||||
level: 'reference_dart',
|
||||
items: [],
|
||||
icon: '/img/icons/javascript.svg',
|
||||
icon: 'docs/img/icons/javascript.svg',
|
||||
},
|
||||
{
|
||||
name: 'supbase-python',
|
||||
url: '/reference/python/start',
|
||||
level: 'reference_python',
|
||||
items: [],
|
||||
icon: '/img/icons/javascript.svg',
|
||||
icon: 'docs/img/icons/javascript.svg',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -693,13 +694,13 @@ export const reference = {
|
||||
name: 'Supabase CLI',
|
||||
url: '/reference/cli/start',
|
||||
items: [],
|
||||
icon: '/img/icons/cli.svg',
|
||||
icon: 'docs/img/icons/cli-icon.svg',
|
||||
},
|
||||
{
|
||||
name: 'Management API',
|
||||
url: '/reference/javascript',
|
||||
items: [],
|
||||
icon: '/img/icons/javascript.svg',
|
||||
icon: 'docs/img/icons/javascript-icon.svg',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { useTheme } from 'common/Providers'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
import { memo, useEffect } from 'react'
|
||||
import { menuState } from '~/hooks/useMenuState'
|
||||
import NavigationMenuGuideList from './NavigationMenuGuideList'
|
||||
import NavigationMenuRefList from './NavigationMenuRefList'
|
||||
import { menuState } from '~/hooks/useMenuState'
|
||||
|
||||
// @ts-expect-error
|
||||
import spec_js_v2 from '~/../../spec/supabase_js_v2.yml' assert { type: 'yml' }
|
||||
// @ts-expect-error
|
||||
@@ -15,16 +11,15 @@ import spec_js_v1 from '~/../../spec/supabase_js_v1.yml' assert { type: 'yml' }
|
||||
import spec_dart_v1 from '~/../../spec/supabase_dart_v1.yml' assert { type: 'yml' }
|
||||
// @ts-expect-error
|
||||
import spec_dart_v0 from '~/../../spec/supabase_dart_v0.yml' assert { type: 'yml' }
|
||||
|
||||
// import { gen_v3 } from '~/lib/refGenerator/helpers'
|
||||
import libCommonSections from '~/../../spec/common-client-libs-sections.json'
|
||||
import apiCommonSections from '~/../../spec/common-api-sections.json'
|
||||
import cliCommonSections from '~/../../spec/common-cli-sections.json'
|
||||
import libCommonSections from '~/../../spec/common-client-libs-sections.json'
|
||||
import authServerCommonSections from '~/../../spec/common-self-hosting-auth-sections.json'
|
||||
import storageServerCommonSections from '~/../../spec/common-self-hosting-storage-sections.json'
|
||||
import realtimeServerCommonSections from '~/../../spec/common-self-hosting-realtime-sections.json'
|
||||
import storageServerCommonSections from '~/../../spec/common-self-hosting-storage-sections.json'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import { useMenuLevelId } from '~/hooks/useMenuState'
|
||||
import NavigationMenuHome from './HomeMenu'
|
||||
|
||||
// Filter libCommonSections for just the relevant sections in the current library
|
||||
function generateAllowedClientLibKeys(sections, spec) {
|
||||
@@ -69,9 +64,6 @@ export type RefKeyOptions =
|
||||
|
||||
const SideNav = () => {
|
||||
const router = useRouter()
|
||||
const { isDarkMode } = useTheme()
|
||||
|
||||
const level = useMenuLevelId()
|
||||
|
||||
let version = ''
|
||||
|
||||
@@ -161,225 +153,24 @@ const SideNav = () => {
|
||||
}
|
||||
}, [router.events])
|
||||
|
||||
const home = [
|
||||
[
|
||||
{
|
||||
label: 'Home',
|
||||
icon: '/img/icons/menu/home',
|
||||
href: '/',
|
||||
level: 'home',
|
||||
},
|
||||
{
|
||||
label: 'Getting Started',
|
||||
icon: '/img/icons/menu/getting-started',
|
||||
href: '/guides/getting-started',
|
||||
level: 'gettingstarted',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Database',
|
||||
icon: '/img/icons/menu/database',
|
||||
href: '/guides/database',
|
||||
level: 'database',
|
||||
},
|
||||
{
|
||||
label: 'Auth',
|
||||
icon: '/img/icons/menu/auth',
|
||||
href: '/guides/auth/overview',
|
||||
level: 'auth',
|
||||
},
|
||||
{
|
||||
label: 'Edge Functions',
|
||||
icon: '/img/icons/menu/functions',
|
||||
href: '/guides/functions',
|
||||
level: 'functions',
|
||||
},
|
||||
{
|
||||
label: 'Realtime',
|
||||
icon: '/img/icons/menu/realtime',
|
||||
href: '/guides/realtime',
|
||||
level: 'realtime',
|
||||
},
|
||||
{
|
||||
label: 'Storage',
|
||||
icon: '/img/icons/menu/storage',
|
||||
href: '/guides/storage',
|
||||
level: 'storage',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Platform',
|
||||
icon: '/img/icons/menu/platform',
|
||||
href: '/guides/platform',
|
||||
level: 'platform',
|
||||
},
|
||||
{
|
||||
label: 'Resources',
|
||||
icon: '/img/icons/menu/platform',
|
||||
href: '/guides/resources',
|
||||
level: 'resources',
|
||||
},
|
||||
{
|
||||
label: 'Integrations',
|
||||
icon: '/img/icons/menu/integrations',
|
||||
href: '/guides/integrations',
|
||||
level: 'integrations',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Client Library Reference',
|
||||
},
|
||||
{
|
||||
label: 'JavaScript',
|
||||
icon: '/img/icons/menu/reference-javascript',
|
||||
hasLightIcon: true,
|
||||
href: '/reference/javascript/introduction',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
{
|
||||
label: 'Flutter',
|
||||
icon: '/img/icons/menu/reference-dart',
|
||||
hasLightIcon: true,
|
||||
href: '/reference/dart/introduction',
|
||||
level: 'reference_dart',
|
||||
},
|
||||
{
|
||||
label: 'Tools Reference',
|
||||
},
|
||||
{
|
||||
label: 'Management API',
|
||||
icon: '/img/icons/menu/reference-api',
|
||||
hasLightIcon: true,
|
||||
href: '/reference/api/introduction',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
{
|
||||
label: 'Supabase CLI',
|
||||
icon: '/img/icons/menu/reference-cli',
|
||||
hasLightIcon: true,
|
||||
href: '/reference/cli/introduction',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
// {
|
||||
// label: 'Self-Hosting Reference',
|
||||
// },
|
||||
// {
|
||||
// label: 'Self-Hosting Auth',
|
||||
// icon: '/img/icons/menu/reference-auth',
|
||||
// hasLightIcon: true,
|
||||
// href: '/reference/self-hosting-auth/introduction',
|
||||
// level: 'reference_self_hosting_auth',
|
||||
// },
|
||||
// {
|
||||
// label: 'Self-Hosting Storage',
|
||||
// hasLightIcon: true,
|
||||
// icon: '/img/icons/menu/reference-storage',
|
||||
// href: '/reference/self-hosting-storage/introduction',
|
||||
// level: 'reference_self_hosting_auth',
|
||||
// },
|
||||
// {
|
||||
// label: 'Self-Hosting Realtime',
|
||||
// hasLightIcon: true,
|
||||
// icon: '/img/icons/menu/reference-realtime',
|
||||
// href: '/reference/self-hosting-realtime/introduction',
|
||||
// level: 'reference_self_hosting_auth',
|
||||
// },
|
||||
],
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="flex relative">
|
||||
{/* // main menu */}
|
||||
<div
|
||||
className={[
|
||||
'transition-all duration-150 ease-out',
|
||||
level === 'home' || !level
|
||||
? 'opacity-100 ml-0 delay-150'
|
||||
: 'opacity-0 -ml-8 invisible absolute',
|
||||
].join(' ')}
|
||||
>
|
||||
<ul className="relative w-full flex flex-col gap-4">
|
||||
{home.map((section, sectionIndex) => {
|
||||
return (
|
||||
<>
|
||||
{sectionIndex !== 0 && (
|
||||
<div
|
||||
className="h-px w-full bg-blackA-300 dark:bg-whiteA-300"
|
||||
key={`section-${sectionIndex}-border`}
|
||||
></div>
|
||||
)}
|
||||
<div key={`section-${sectionIndex}`}>
|
||||
<div className="flex flex-col gap-3">
|
||||
{section.map((link) => {
|
||||
if (!link.href) {
|
||||
return (
|
||||
<span
|
||||
key={link.label}
|
||||
className="font-mono uppercase text-xs text-scale-900"
|
||||
>
|
||||
{link.label}
|
||||
</span>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Link href={link.href} passHref key={link.label}>
|
||||
<a>
|
||||
<li
|
||||
className={[
|
||||
'group flex items-center gap-3',
|
||||
'text-base transition-all duration-150 text-scale-1200 hover:text-brand-900 hover:cursor-pointer ',
|
||||
].join(' ')}
|
||||
>
|
||||
<Image
|
||||
alt={link.label}
|
||||
src={`${router.basePath}${
|
||||
Object.hasOwn(link, 'hasLightIcon') && !link.hasLightIcon
|
||||
? link.icon
|
||||
: isDarkMode
|
||||
? link.icon
|
||||
: `${link.icon}-light`
|
||||
}${!link.icon.includes('png') ? '.svg' : ''}`}
|
||||
width={17}
|
||||
height={17}
|
||||
className="w-4 h-4 group-hover:scale-110 ease-out transition-all"
|
||||
/>
|
||||
{link.label}
|
||||
</li>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<NavigationMenuGuideList id={'gettingstarted'} currentLevel={level} />
|
||||
<NavigationMenuGuideList id={'database'} currentLevel={level} />
|
||||
<NavigationMenuGuideList id={'auth'} currentLevel={level} />
|
||||
<NavigationMenuGuideList id={'functions'} currentLevel={level} />
|
||||
<NavigationMenuGuideList id={'realtime'} currentLevel={level} />
|
||||
<NavigationMenuGuideList id={'storage'} currentLevel={level} />
|
||||
<NavigationMenuGuideList id={'platform'} currentLevel={level} />
|
||||
<NavigationMenuGuideList
|
||||
id={'resources'}
|
||||
currentLevel={level}
|
||||
setMenuLevelId={menuState.setMenuLevelId}
|
||||
/>
|
||||
<NavigationMenuGuideList id={'integrations'} currentLevel={level} />
|
||||
<NavigationMenuGuideList id={'reference'} currentLevel={level} />
|
||||
<NavigationMenuHome />
|
||||
<NavigationMenuGuideList id={'gettingstarted'} />
|
||||
<NavigationMenuGuideList id={'database'} />
|
||||
<NavigationMenuGuideList id={'auth'} />
|
||||
<NavigationMenuGuideList id={'functions'} />
|
||||
<NavigationMenuGuideList id={'realtime'} />
|
||||
<NavigationMenuGuideList id={'storage'} />
|
||||
<NavigationMenuGuideList id={'platform'} />
|
||||
<NavigationMenuGuideList id={'resources'} />
|
||||
<NavigationMenuGuideList id={'integrations'} />
|
||||
<NavigationMenuGuideList id={'reference'} />
|
||||
{/* // Client Libs */}
|
||||
<NavigationMenuRefList
|
||||
key={'reference-js-menu'}
|
||||
id={'reference_javascript_v1'}
|
||||
currentLevel={level}
|
||||
commonSections={libCommonSections}
|
||||
lib="javascript"
|
||||
allowedClientKeys={generateAllowedClientLibKeys(libCommonSections, spec_js_v1)}
|
||||
@@ -387,7 +178,6 @@ const SideNav = () => {
|
||||
<NavigationMenuRefList
|
||||
key={'reference-js-menu'}
|
||||
id={'reference_javascript_v2'}
|
||||
currentLevel={level}
|
||||
commonSections={libCommonSections}
|
||||
lib="javascript"
|
||||
allowedClientKeys={generateAllowedClientLibKeys(libCommonSections, spec_js_v2)}
|
||||
@@ -395,7 +185,6 @@ const SideNav = () => {
|
||||
<NavigationMenuRefList
|
||||
key={'reference-dart-menu'}
|
||||
id={'reference_dart_v0'}
|
||||
currentLevel={level}
|
||||
commonSections={libCommonSections}
|
||||
lib="dart"
|
||||
allowedClientKeys={generateAllowedClientLibKeys(libCommonSections, spec_dart_v0)}
|
||||
@@ -403,7 +192,6 @@ const SideNav = () => {
|
||||
<NavigationMenuRefList
|
||||
key={'reference-dart-menu'}
|
||||
id={'reference_dart_v1'}
|
||||
currentLevel={level}
|
||||
commonSections={libCommonSections}
|
||||
lib="dart"
|
||||
allowedClientKeys={generateAllowedClientLibKeys(libCommonSections, spec_dart_v1)}
|
||||
@@ -412,14 +200,12 @@ const SideNav = () => {
|
||||
<NavigationMenuRefList
|
||||
key={'reference-cli-menu'}
|
||||
id={'reference_cli'}
|
||||
currentLevel={level}
|
||||
commonSections={cliCommonSections}
|
||||
lib="cli"
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-api-menu'}
|
||||
id={'reference_api'}
|
||||
currentLevel={level}
|
||||
commonSections={apiCommonSections}
|
||||
lib="api"
|
||||
/>
|
||||
@@ -427,21 +213,18 @@ const SideNav = () => {
|
||||
<NavigationMenuRefList
|
||||
key={'reference-self-hosting-auth-menu'}
|
||||
id={'reference_self_hosting_auth'}
|
||||
currentLevel={level}
|
||||
commonSections={authServerCommonSections}
|
||||
lib="self-hosting-auth"
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-self-hosting-storage-menu'}
|
||||
id={'reference_self_hosting_storage'}
|
||||
currentLevel={level}
|
||||
commonSections={storageServerCommonSections}
|
||||
lib="self-hosting-storage"
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-self-hosting-realtime-menu'}
|
||||
id={'reference_self_hosting_realtime'}
|
||||
currentLevel={level}
|
||||
commonSections={realtimeServerCommonSections}
|
||||
lib="self-hosting-auth"
|
||||
/>
|
||||
|
||||
@@ -5,14 +5,14 @@ import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import React from 'react'
|
||||
import { IconChevronLeft } from '~/../../packages/ui'
|
||||
import { useMenuLevelId } from '~/hooks/useMenuState'
|
||||
import * as NavItems from './NavigationMenu.constants'
|
||||
|
||||
interface Props {
|
||||
currentLevel: string
|
||||
id: string
|
||||
setMenuLevelId?: any
|
||||
}
|
||||
const NavigationMenuGuideList: React.FC<Props> = ({ currentLevel, id, setMenuLevelId }) => {
|
||||
const NavigationMenuGuideList: React.FC<Props> = ({ id, setMenuLevelId }) => {
|
||||
const router = useRouter()
|
||||
const { isDarkMode } = useTheme()
|
||||
|
||||
@@ -25,6 +25,8 @@ const NavigationMenuGuideList: React.FC<Props> = ({ currentLevel, id, setMenuLev
|
||||
// remove the end of the url if a deep link
|
||||
const firstLevelRoute = url?.split('/')?.slice(0, 4)?.join('/')
|
||||
|
||||
const level = useMenuLevelId()
|
||||
|
||||
return (
|
||||
<Accordion.Root
|
||||
collapsible
|
||||
@@ -34,12 +36,12 @@ const NavigationMenuGuideList: React.FC<Props> = ({ currentLevel, id, setMenuLev
|
||||
className={[
|
||||
'transition-all ml-8 duration-150 ease-out',
|
||||
// enabled
|
||||
currentLevel === id && 'opacity-100 ml-0 delay-150',
|
||||
currentLevel === 'home' && 'ml-12',
|
||||
level === id && 'opacity-100 ml-0 delay-150',
|
||||
level === 'home' && 'ml-12',
|
||||
|
||||
// disabled
|
||||
currentLevel !== 'home' && currentLevel !== id ? '-ml-8' : '',
|
||||
currentLevel !== id ? 'opacity-0 invisible absolute' : '',
|
||||
level !== 'home' && level !== id ? '-ml-8' : '',
|
||||
level !== id ? 'opacity-0 invisible absolute' : '',
|
||||
].join(' ')}
|
||||
>
|
||||
<ul className={['relative w-full flex flex-col gap-0'].join(' ')}>
|
||||
@@ -81,7 +83,7 @@ const NavigationMenuGuideList: React.FC<Props> = ({ currentLevel, id, setMenuLev
|
||||
: 'hover:text-brand-900 text-scale-1200',
|
||||
].join(' ')}
|
||||
>
|
||||
{menu.title ?? currentLevel}
|
||||
{menu.title ?? level}
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
@@ -105,24 +107,11 @@ const NavigationMenuGuideList: React.FC<Props> = ({ currentLevel, id, setMenuLev
|
||||
}
|
||||
|
||||
const LinkContainer = (props) => {
|
||||
if (props.url) {
|
||||
return (
|
||||
<Link href={props.url} passHref>
|
||||
<a className={props.className}>{props.children}</a>
|
||||
</Link>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<button
|
||||
className={props.className}
|
||||
onClick={() => {
|
||||
if (props.parent && setMenuLevelId) setMenuLevelId(props.parent)
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Link href={props.url} passHref>
|
||||
<a className={props.className}>{props.children}</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -148,25 +137,19 @@ const NavigationMenuGuideList: React.FC<Props> = ({ currentLevel, id, setMenuLev
|
||||
].join(' ')}
|
||||
parent={subItem.parent}
|
||||
>
|
||||
{subItem.icon && <img src={`${subItem.icon}.svg`} className="w-3" />}
|
||||
{subItem.icon && (
|
||||
<Image
|
||||
alt={subItem.name + router.basePath}
|
||||
src={
|
||||
`${router.basePath}` +
|
||||
`${subItem.icon}${!isDarkMode ? '-light' : ''}.svg`
|
||||
}
|
||||
width={15}
|
||||
height={15}
|
||||
/>
|
||||
)}
|
||||
{subItem.name}
|
||||
</LinkContainer>
|
||||
{/* <Link href={`${subItem.url}`} passHref>
|
||||
<a
|
||||
className={[
|
||||
'flex items-center gap-2',
|
||||
'cursor-pointer transition text-sm',
|
||||
subItem.url === router.pathname
|
||||
? 'text-brand-900'
|
||||
: 'hover:text-brand-900 text-scale-1000',
|
||||
].join(' ')}
|
||||
>
|
||||
{subItem.icon && (
|
||||
<img src={`${subItem.icon}.svg`} className="w-3" />
|
||||
)}
|
||||
{subItem.name}
|
||||
</a>
|
||||
</Link> */}
|
||||
</li>
|
||||
|
||||
{subItem.items && subItem.items.length > 0 && (
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useTheme } from 'common/Providers'
|
||||
// import apiCommonSections from '~/../../spec/common-client-libs-sections.json'
|
||||
|
||||
import RevVersionDropdown from '~/components/RefVersionDropdown'
|
||||
import { useMenuActiveRefId } from '~/hooks/useMenuState'
|
||||
import { useMenuActiveRefId, useMenuLevelId } from '~/hooks/useMenuState'
|
||||
import { RefIdOptions, RefKeyOptions } from './NavigationMenu'
|
||||
|
||||
const FunctionLink = ({
|
||||
@@ -32,10 +32,20 @@ const FunctionLink = ({
|
||||
const router = useRouter()
|
||||
const activeAccordianItem = useMenuActiveRefId()
|
||||
|
||||
// check if we're on a versioned page
|
||||
let version = ''
|
||||
if (router.asPath.includes('v1')) {
|
||||
version = 'v1'
|
||||
}
|
||||
|
||||
if (router.asPath.includes('v0')) {
|
||||
version = 'v0'
|
||||
}
|
||||
|
||||
const active = activeAccordianItem === id
|
||||
return (
|
||||
<li key={id} className="function-link-item">
|
||||
<Link href={`/reference/${library}/${slug}`} passHref>
|
||||
<Link href={`/reference/${library}/${version ? version + '/' : ''}${slug}`} passHref>
|
||||
<a
|
||||
className={[
|
||||
'cursor-pointer transition text-sm hover:text-brand-900 flex gap-3',
|
||||
@@ -63,8 +73,6 @@ const Divider = () => {
|
||||
}
|
||||
|
||||
interface INavigationMenuRefList {
|
||||
currentLevel: string
|
||||
|
||||
id: RefIdOptions
|
||||
lib: RefKeyOptions
|
||||
commonSections: any[] // to do type up
|
||||
@@ -75,7 +83,6 @@ interface INavigationMenuRefList {
|
||||
}
|
||||
|
||||
const NavigationMenuRefList: React.FC<INavigationMenuRefList> = ({
|
||||
currentLevel,
|
||||
id,
|
||||
lib,
|
||||
commonSections,
|
||||
@@ -133,17 +140,22 @@ const NavigationMenuRefList: React.FC<INavigationMenuRefList> = ({
|
||||
}).items.map((x) => x.id)
|
||||
: []
|
||||
|
||||
// console.log(filterIds)
|
||||
// console.log(modifierIds)
|
||||
|
||||
const level = useMenuLevelId()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
'transition-all ml-8 duration-150 ease-out',
|
||||
// enabled
|
||||
currentLevel === id && 'opacity-100 ml-0 delay-150 h-auto',
|
||||
level === id && 'opacity-100 ml-0 delay-150 h-auto',
|
||||
// move menu back to margin-left
|
||||
currentLevel === 'home' && 'ml-12',
|
||||
level === 'home' && 'ml-12',
|
||||
// disabled
|
||||
currentLevel !== 'home' && currentLevel !== id ? '-ml-8' : '',
|
||||
currentLevel !== id ? 'opacity-0 invisible absolute h-0 overflow-hidden' : '',
|
||||
level !== 'home' && level !== id ? '-ml-8' : '',
|
||||
level !== id ? 'opacity-0 invisible absolute h-0 overflow-hidden' : '',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className={'w-full flex flex-col gap-0 sticky top-8'}>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// @ts-expect-error
|
||||
import spec from '~/../../spec/cli_v1_commands.yaml' assert { type: 'yml' }
|
||||
import CodeBlock from '~/components/CodeBlock/CodeBlock'
|
||||
import Options from '~/components/Options'
|
||||
import Param from '~/components/Params'
|
||||
@@ -46,7 +48,7 @@ export type Command = {
|
||||
}
|
||||
|
||||
const CliCommandSection = (props) => {
|
||||
const command = props.spec.commands.find((x: any) => x.id === props.funcData.id)
|
||||
const command = spec.commands.find((x: any) => x.id === props.funcData.id)
|
||||
|
||||
return (
|
||||
<RefSubLayout.Section
|
||||
@@ -72,10 +74,10 @@ const CliCommandSection = (props) => {
|
||||
</header>
|
||||
|
||||
{/* {command.usage && (
|
||||
<CodeBlock language="bash" className="relative">
|
||||
{command.usage}
|
||||
</CodeBlock>
|
||||
)} */}
|
||||
<CodeBlock language="bash" className="relative">
|
||||
{command.usage}
|
||||
</CodeBlock>
|
||||
)} */}
|
||||
|
||||
{command.subcommands.length > 0 && (
|
||||
<div className="">
|
||||
|
||||
@@ -65,7 +65,7 @@ const RefSectionHandler = (props) => {
|
||||
/>
|
||||
)
|
||||
case 'cli-command':
|
||||
return <CliCommandSection funcData={x} commonFuncData={x} spec={props.spec} />
|
||||
return <CliCommandSection funcData={x} commonFuncData={x} />
|
||||
break
|
||||
case 'operation':
|
||||
return <ApiOperationSection funcData={x} commonFuncData={x} spec={props.spec} />
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
},
|
||||
{
|
||||
"text": "SupaSquad",
|
||||
"url": "/docs/handbook/supasquad"
|
||||
"url": "/supasquad"
|
||||
},
|
||||
{
|
||||
"text": "DevTo",
|
||||
|
||||
@@ -9,6 +9,7 @@ export const menuState = proxy({
|
||||
},
|
||||
menuLevelId: '',
|
||||
setMenuLevelId: (value) => {
|
||||
menuState.menuMobileOpen = false
|
||||
menuState.menuLevelId = value
|
||||
},
|
||||
menuMobileOpen: false,
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { MDXProvider } from '@mdx-js/react'
|
||||
import Head from 'next/head'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { FC } from 'react'
|
||||
import components from '~/components'
|
||||
import Footer from '~/components/Footer'
|
||||
import SideBar from '~/components/Navigation/SideBar'
|
||||
import TableOfContents from '~/components/TableOfContents'
|
||||
import NavBar from '~/components/Navigation/NavBar'
|
||||
|
||||
interface Props {
|
||||
meta: { title: string; description?: string; hide_table_of_contents?: boolean; video?: string }
|
||||
@@ -15,34 +12,6 @@ interface Props {
|
||||
}
|
||||
|
||||
const Layout: FC<Props> = (props: Props) => {
|
||||
// const contentString = renderToString(props.children)
|
||||
|
||||
const [active, setActive] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const key = localStorage.getItem('supabaseDarkMode')
|
||||
if (!key) {
|
||||
// Default to dark mode if no preference config
|
||||
document.documentElement.className = 'dark'
|
||||
} else {
|
||||
document.documentElement.className = key === 'true' ? 'dark' : ''
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(function () {
|
||||
setActive(true)
|
||||
}, 150)
|
||||
}, [])
|
||||
|
||||
// const contentString = renderToString(props.children)
|
||||
|
||||
// const content = serialize(contentString || '')
|
||||
|
||||
// console.log('contentString', contentString)
|
||||
|
||||
// const _toc = toc('#hello world', { maxdepth: 1, firsth1: false })
|
||||
|
||||
const hasTableOfContents =
|
||||
props.toc !== undefined &&
|
||||
props.toc.json.filter((item) => item.lvl !== 1 && item.lvl <= 3).length > 0
|
||||
@@ -60,13 +29,7 @@ const Layout: FC<Props> = (props: Props) => {
|
||||
<meta property="og:title" content={props.meta?.title} />
|
||||
</Head>
|
||||
|
||||
<div
|
||||
className={[
|
||||
'relative transition-all ease-out',
|
||||
'duration-150 ',
|
||||
active ? 'opacity-100 left-0' : 'opacity-0 left-5',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className={['relative transition-all ease-out', 'duration-150 '].join(' ')}>
|
||||
{/* <p className="text-brand-900 tracking-wider">Tutorials</p> */}
|
||||
<article className="prose dark:prose-dar max-w-none">
|
||||
<h1>{props.meta.title}</h1>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useTheme } from 'common/Providers'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import Footer from '~/components/Footer'
|
||||
import NavigationMenu from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import TopNavBar from '~/components/Navigation/NavigationMenu/TopNavBar'
|
||||
import SiteRefLayout from './SiteRefLayout'
|
||||
import Image from 'next/image'
|
||||
import TopNavBarRef from '~/components/Navigation/NavigationMenu/TopNavBarRef'
|
||||
|
||||
const Layout = (props) => {
|
||||
const router = useRouter()
|
||||
import FooterHelpCallout from '~/components/FooterHelpCallout'
|
||||
|
||||
import { menuState, useMenuLevelId, useMenuMobileOpen } from '~/hooks/useMenuState'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
const SiteRefLayout = ({ children }) => {
|
||||
const { isDarkMode } = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
const key = localStorage.getItem('supabaseDarkMode')
|
||||
@@ -20,50 +23,298 @@ const Layout = (props) => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (process.env.NEXT_PUBLIC_NEW_DOCS !== 'true') {
|
||||
return <>{props.children}</>
|
||||
}
|
||||
const menuLevel = useMenuLevelId()
|
||||
const mobileMenuOpen = useMenuMobileOpen()
|
||||
|
||||
return SiteRefLayout(props)
|
||||
|
||||
if (process.env.NEXT_PUBLIC_EXPERIMENTAL_REF && router.asPath.includes('/reference/')) {
|
||||
return SiteRefLayout(props)
|
||||
const levelsData = {
|
||||
home: {
|
||||
icon: '/docs/img/icons/menu/home',
|
||||
name: 'Home',
|
||||
},
|
||||
gettingstarted: {
|
||||
icon: '/docs/img/icons/menu/getting-started',
|
||||
name: 'Getting Started',
|
||||
},
|
||||
database: {
|
||||
icon: '/docs/img/icons/menu/database',
|
||||
name: 'Database',
|
||||
},
|
||||
auth: {
|
||||
icon: '/docs/img/icons/menu/auth',
|
||||
name: 'Auth',
|
||||
},
|
||||
functions: {
|
||||
icon: '/docs/img/icons/menu/functions',
|
||||
name: 'Functions',
|
||||
},
|
||||
realtime: {
|
||||
icon: '/docs/img/icons/menu/realtime',
|
||||
name: 'Realtime',
|
||||
},
|
||||
storage: {
|
||||
icon: '/docs/img/icons/menu/storage',
|
||||
name: 'Storage',
|
||||
},
|
||||
platform: {
|
||||
icon: '/docs/img/icons/menu/platform',
|
||||
name: 'Platform',
|
||||
},
|
||||
resources: {
|
||||
icon: '/docs/img/icons/menu/resources',
|
||||
name: 'Resources',
|
||||
},
|
||||
integrations: {
|
||||
icon: '/docs/img/icons/menu/integrations',
|
||||
name: 'Integrations',
|
||||
},
|
||||
reference_javascript_v1: {
|
||||
icon: '/docs/img/icons/menu/reference-javascript',
|
||||
name: 'Javascript Reference v1.0',
|
||||
},
|
||||
reference_javascript_v2: {
|
||||
icon: '/docs/img/icons/menu/reference-javascript',
|
||||
name: 'Javascript Reference v2.0',
|
||||
},
|
||||
reference_dart_v0: {
|
||||
icon: '/docs/img/icons/menu/reference-dart',
|
||||
name: 'Dart Reference v0.0',
|
||||
},
|
||||
reference_dart_v1: {
|
||||
icon: '/docs/img/icons/menu/reference-dart',
|
||||
name: 'Dart Reference v0.0',
|
||||
},
|
||||
reference_cli: {
|
||||
icon: '/docs/img/icons/menu/reference-cli',
|
||||
name: 'CLI Reference',
|
||||
},
|
||||
reference_api: {
|
||||
icon: '/docs/img/icons/menu/reference-api',
|
||||
name: 'Management API Reference',
|
||||
},
|
||||
reference_self_hosting_auth: {
|
||||
icon: '/docs/img/icons/menu/reference-auth',
|
||||
name: 'Auth Server Reference',
|
||||
},
|
||||
reference_self_hosting_storage: {
|
||||
icon: '/docs/img/icons/menu/reference-storage',
|
||||
name: 'Storage Server Reference',
|
||||
},
|
||||
reference_self_hosting_realtime: {
|
||||
icon: '/docs/img/icons/menu/reference-realtime',
|
||||
name: 'Realtime Server Reference',
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
<Image
|
||||
width={608}
|
||||
height={975}
|
||||
alt="background"
|
||||
<main>
|
||||
{/* <img
|
||||
src={`${router.basePath}/img/gradient-bg.png`}
|
||||
className="absolute left-0 w-[520px] top-0 -z-10"
|
||||
/>
|
||||
<div className="mx-auto sticky top-0 z-10">
|
||||
<TopNavBar />
|
||||
</div>
|
||||
<div className="grid grid-cols-12 opacity-100 duration-100 max-w-[1400px] mx-auto py-16 gap-4 px-5">
|
||||
<div className="col-span-3">
|
||||
<NavigationMenu />
|
||||
</div>
|
||||
<div className="col-span-9">
|
||||
{props.children}
|
||||
<hr className="border-scale-400 mt-32"></hr>
|
||||
<div className="flex flex-row gap-3 mt-6">
|
||||
<span className="text-xs text-scale-900">Supabase 2022</span>
|
||||
<span className="text-xs text-scale-900">—</span>
|
||||
<a className="text-xs text-scale-800">Contributing</a>
|
||||
<a className="text-xs text-scale-800">Author Styleguide</a>
|
||||
<a className="text-xs text-scale-800">Changelog</a>
|
||||
<a className="text-xs text-scale-800">Opensource</a>
|
||||
<a className="text-xs text-scale-800">Supasquad</a>
|
||||
className="absolute left-0 w-[520px] top-0 -z-1 pointer-events-none"
|
||||
/> */}
|
||||
|
||||
<div className="flex flex-row h-screen">
|
||||
<div
|
||||
className={[
|
||||
'hidden', // experiment hidden
|
||||
'transition-all',
|
||||
'top-0',
|
||||
'relative',
|
||||
'h-screen w-[420px]',
|
||||
'z-10',
|
||||
'lg:flex flex-col ml-0',
|
||||
].join(' ')}
|
||||
>
|
||||
<div
|
||||
className={[
|
||||
'top-0',
|
||||
'relative',
|
||||
'w-auto',
|
||||
'z-10',
|
||||
|
||||
'border-r overflow-auto h-screen',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300',
|
||||
'flex flex-col',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="top-0 sticky z-10">
|
||||
<div>
|
||||
<div>
|
||||
<div
|
||||
className={[
|
||||
'hidden md:flex md:height-auto',
|
||||
'pt-8 bg-scale-200 flex-col gap-8',
|
||||
].join(' ')}
|
||||
>
|
||||
<Link href="/">
|
||||
<a className="px-10 flex items-center gap-2">
|
||||
<Image
|
||||
className="cursor-pointer"
|
||||
src={isDarkMode ? '/docs/supabase-dark.svg' : '/docs/supabase-light.svg'}
|
||||
width={96}
|
||||
height={24}
|
||||
alt="Supabase Logo"
|
||||
/>
|
||||
<span className="font-mono text-sm font-medium text-brand-900">DOCS</span>
|
||||
</a>
|
||||
</Link>
|
||||
{/* {router.asPath.includes('/reference/') && <RefSwitcher />} */}
|
||||
</div>
|
||||
<div className="h-4 bg-scale-200 w-full"></div>
|
||||
<div className="bg-gradient-to-b from-scale-200 to-transparent h-4 w-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={['pb-10 px-5 md:px-10 md:flex'].join(' ')}>
|
||||
<NavigationMenu />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
<div
|
||||
// #docs-content-container is used by layout to scroll to top
|
||||
id="docs-content-container"
|
||||
className={[
|
||||
'w-full h-screen transition-all ease-out',
|
||||
mobileMenuOpen ? 'overflow-hidden' : 'overflow-auto',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="flex flex-col relative">
|
||||
{/* <NavigationMenu /> */}
|
||||
<div className={['lg:sticky top-0 z-10 overflow-hidden'].join(' ')}>
|
||||
<TopNavBarRef />
|
||||
</div>
|
||||
<div
|
||||
className={[
|
||||
'sticky transition-all top-0',
|
||||
'z-10',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className={['lg:hidden', 'px-5 ', 'border-b z-10'].join(' ')}>
|
||||
<div
|
||||
className={[
|
||||
'transition-all ease-out z-10',
|
||||
'top-0',
|
||||
mobileMenuOpen && 'absolute',
|
||||
'flex items-center h-[40px]',
|
||||
mobileMenuOpen ? 'gap-0' : 'gap-3',
|
||||
].join(' ')}
|
||||
>
|
||||
<button
|
||||
className={['mr-2', mobileMenuOpen && 'mt-0.5'].join(' ')}
|
||||
onClick={() => menuState.setMenuMobileOpen(!mobileMenuOpen)}
|
||||
>
|
||||
<div
|
||||
className={[
|
||||
'space-y-1 group cursor-pointer relative',
|
||||
mobileMenuOpen ? 'w-4 h-4' : 'w-4 h-[8px]',
|
||||
].join(' ')}
|
||||
>
|
||||
<span
|
||||
className={[
|
||||
'transition-all ease-out block w-4 h-px bg-scale-900 group-hover:bg-scale-1200',
|
||||
!mobileMenuOpen ? 'w-4' : 'absolute rotate-45 top-[6px]',
|
||||
].join(' ')}
|
||||
></span>
|
||||
<span
|
||||
className={[
|
||||
'transition-all ease-out block h-px bg-scale-900 group-hover:bg-scale-1200',
|
||||
!mobileMenuOpen
|
||||
? 'w-3 group-hover:w-4'
|
||||
: 'absolute w-4 -rotate-45 top-[2px]',
|
||||
].join(' ')}
|
||||
></span>
|
||||
</div>
|
||||
</button>
|
||||
<div className={[].join(' ')}>
|
||||
<img
|
||||
src={
|
||||
menuLevel
|
||||
? levelsData[menuLevel].icon + '.svg'
|
||||
: levelsData['home'].icon + '.svg'
|
||||
}
|
||||
className={[
|
||||
'transition-all duration-200',
|
||||
mobileMenuOpen ? 'invisible w-0 h-0' : 'w-4 h-4',
|
||||
].join(' ')}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className={[
|
||||
'transition-all duration-200',
|
||||
'text-scale-1200',
|
||||
mobileMenuOpen ? 'text-xs' : 'text-sm',
|
||||
].join(' ')}
|
||||
>
|
||||
{mobileMenuOpen
|
||||
? 'Close'
|
||||
: menuLevel
|
||||
? levelsData[menuLevel].name
|
||||
: levelsData['home'].name}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={[
|
||||
'transition-all ease-out duration-200',
|
||||
'absolute left-0 right-0 h-screen',
|
||||
'px-5 pl-5 py-16',
|
||||
'top-[0px]',
|
||||
'bg-scale-200',
|
||||
mobileMenuOpen
|
||||
? 'overflow-y-auto opacity-100 left-[0px] visible'
|
||||
: 'left-[-40px] h-0 opacity-0 invisible',
|
||||
].join(' ')}
|
||||
>
|
||||
<NavigationMenu />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grow px-5 max-w-7xl mx-auto py-16">
|
||||
{children}
|
||||
|
||||
<div className="mt-32">
|
||||
<FooterHelpCallout />
|
||||
</div>
|
||||
<hr className="border-scale-400 mt-8"></hr>
|
||||
<div className="flex flex-col lg:flex-row gap-3 mt-6">
|
||||
<span className="text-xs text-scale-900">Supabase 2022</span>
|
||||
<span className="text-xs text-scale-900">—</span>
|
||||
<Link href="/handbook/contributing">
|
||||
<a className="text-xs text-scale-800 hover:underline">Contributing</a>
|
||||
</Link>
|
||||
<Link href="https://supabase.com/changelog">
|
||||
<a className="text-xs text-scale-800 hover:underline">Changelog</a>
|
||||
</Link>
|
||||
|
||||
<Link href="https://github.com/supabase/supabase/blob/master/DEVELOPERS.md">
|
||||
<a className="text-xs text-scale-800 hover:underline">Author Styleguide</a>
|
||||
</Link>
|
||||
<Link href="https://supabase.com/docs/oss">
|
||||
<a className="text-xs text-scale-800 hover:underline">Open Source</a>
|
||||
</Link>
|
||||
<Link href="https://supabase.com/supasquad">
|
||||
<a className="text-xs text-scale-800 hover:underline">SupaSquad</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={[
|
||||
// 'top-0 left-0',
|
||||
'hidden', // experiment hidden
|
||||
'h-full',
|
||||
'left-0',
|
||||
'right-0',
|
||||
'z-10',
|
||||
'backdrop-blur-sm backdrop-filter bg-white-1200 dark:bg-blackA-600',
|
||||
mobileMenuOpen ? 'absolute MOBILE-MENU-OPEN' : 'hidden h-0 MOBILE-MENU-CLOSED',
|
||||
].join(' ')}
|
||||
onClick={() => menuState.setMenuMobileOpen(!mobileMenuOpen)}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout
|
||||
export default SiteRefLayout
|
||||
@@ -1,27 +1,31 @@
|
||||
import { useTheme } from 'common/Providers'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import NavigationMenu from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import TopNavBarRef from '~/components/Navigation/NavigationMenu/TopNavBarRef'
|
||||
import { useTheme } from 'common/Providers'
|
||||
|
||||
import RefSwitcher from '~/components/Navigation/RefSwitcher'
|
||||
import { useEffect, useState, memo } from 'react'
|
||||
|
||||
import FooterHelpCallout from '~/components/FooterHelpCallout'
|
||||
import { NavigationMenuContextProvider } from '~/components/Navigation/NavigationMenu/NavigationMenu.Context'
|
||||
import { useMenuLevelId, useMenuMobileOpen } from '~/hooks/useMenuState'
|
||||
|
||||
import { menuState } from '~/hooks/useMenuState'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
const SiteRefLayout = ({ children }) => {
|
||||
const router = useRouter()
|
||||
const { isDarkMode } = useTheme()
|
||||
|
||||
const menuLevel = useMenuLevelId()
|
||||
const mobileMenuOpen = useMenuMobileOpen()
|
||||
useEffect(() => {
|
||||
const key = localStorage.getItem('supabaseDarkMode')
|
||||
if (!key) {
|
||||
// Default to dark mode if no preference config
|
||||
document.documentElement.className = 'dark'
|
||||
} else {
|
||||
document.documentElement.className = key === 'true' ? 'dark' : ''
|
||||
}
|
||||
}, [])
|
||||
|
||||
//console.log('menuLevel', menuLevel)
|
||||
const menuLevel = 'home'
|
||||
// useMenuLevelId()
|
||||
const mobileMenuOpen = false
|
||||
|
||||
const levelsData = {
|
||||
home: {
|
||||
@@ -103,218 +107,214 @@ const SiteRefLayout = ({ children }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationMenuContextProvider activeRefItem={''} setActiveRefItem={() => {}}>
|
||||
<main>
|
||||
{/* <img
|
||||
<main>
|
||||
{/* <img
|
||||
src={`${router.basePath}/img/gradient-bg.png`}
|
||||
className="absolute left-0 w-[520px] top-0 -z-1 pointer-events-none"
|
||||
/> */}
|
||||
|
||||
<div className="flex flex-row h-screen">
|
||||
<div className="flex flex-row h-screen">
|
||||
<div
|
||||
className={[
|
||||
'hidden', // experiment hidden
|
||||
'transition-all',
|
||||
'top-0',
|
||||
'relative',
|
||||
'h-screen w-[420px]',
|
||||
'z-10',
|
||||
'lg:flex flex-col ml-0',
|
||||
].join(' ')}
|
||||
>
|
||||
<div
|
||||
className={[
|
||||
'hidden', // experiment hidden
|
||||
'transition-all',
|
||||
'top-0',
|
||||
'relative',
|
||||
'h-screen w-[420px]',
|
||||
'w-auto',
|
||||
'z-10',
|
||||
'lg:flex flex-col ml-0',
|
||||
|
||||
'border-r overflow-auto h-screen',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300',
|
||||
'flex flex-col',
|
||||
].join(' ')}
|
||||
>
|
||||
<div
|
||||
className={[
|
||||
'top-0',
|
||||
'relative',
|
||||
'w-auto',
|
||||
'z-10',
|
||||
|
||||
'border-r overflow-auto h-screen',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300',
|
||||
'flex flex-col',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="top-0 sticky z-10">
|
||||
<div className="top-0 sticky z-10">
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<div
|
||||
className={[
|
||||
'hidden md:flex md:height-auto',
|
||||
'pt-8 bg-scale-200 flex-col gap-8',
|
||||
].join(' ')}
|
||||
>
|
||||
<Link href="/">
|
||||
<a className="px-10 flex items-center gap-2">
|
||||
<Image
|
||||
className="cursor-pointer"
|
||||
src={
|
||||
isDarkMode ? '/docs/supabase-dark.svg' : '/docs/supabase-light.svg'
|
||||
}
|
||||
width={96}
|
||||
height={24}
|
||||
alt="Supabase Logo"
|
||||
/>
|
||||
<span className="font-mono text-sm font-medium text-brand-900">DOCS</span>
|
||||
</a>
|
||||
</Link>
|
||||
{/* {router.asPath.includes('/reference/') && <RefSwitcher />} */}
|
||||
</div>
|
||||
<div className="h-4 bg-scale-200 w-full"></div>
|
||||
<div className="bg-gradient-to-b from-scale-200 to-transparent h-4 w-full"></div>
|
||||
<div
|
||||
className={[
|
||||
'hidden md:flex md:height-auto',
|
||||
'pt-8 bg-scale-200 flex-col gap-8',
|
||||
].join(' ')}
|
||||
>
|
||||
<Link href="/">
|
||||
<a className="px-10 flex items-center gap-2">
|
||||
<Image
|
||||
className="cursor-pointer"
|
||||
src={isDarkMode ? '/docs/supabase-dark.svg' : '/docs/supabase-light.svg'}
|
||||
width={96}
|
||||
height={24}
|
||||
alt="Supabase Logo"
|
||||
/>
|
||||
<span className="font-mono text-sm font-medium text-brand-900">DOCS</span>
|
||||
</a>
|
||||
</Link>
|
||||
{/* {router.asPath.includes('/reference/') && <RefSwitcher />} */}
|
||||
</div>
|
||||
<div className="h-4 bg-scale-200 w-full"></div>
|
||||
<div className="bg-gradient-to-b from-scale-200 to-transparent h-4 w-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={['pb-10 px-5 md:px-10 md:flex'].join(' ')}>
|
||||
<NavigationMenu />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
// #docs-content-container is used by layout to scroll to top
|
||||
id="docs-content-container"
|
||||
className={[
|
||||
'w-full h-screen transition-all ease-out',
|
||||
mobileMenuOpen ? 'overflow-hidden' : 'overflow-auto',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="flex flex-col relative">
|
||||
{/* <NavigationMenu /> */}
|
||||
<div className={['lg:sticky top-0 z-10 overflow-hidden'].join(' ')}>
|
||||
<TopNavBarRef />
|
||||
</div>
|
||||
<div
|
||||
className={[
|
||||
'sticky transition-all top-0',
|
||||
'z-10',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className={['lg:hidden', 'px-5 ', 'border-b z-10'].join(' ')}>
|
||||
<div
|
||||
className={[
|
||||
'transition-all ease-out z-10',
|
||||
'top-0',
|
||||
mobileMenuOpen && 'absolute',
|
||||
'flex items-center h-[40px]',
|
||||
mobileMenuOpen ? 'gap-0' : 'gap-3',
|
||||
].join(' ')}
|
||||
>
|
||||
<button
|
||||
className={['mr-2', mobileMenuOpen && 'mt-0.5'].join(' ')}
|
||||
onClick={() => menuState.setMenuMobileOpen(!mobileMenuOpen)}
|
||||
>
|
||||
<div
|
||||
className={[
|
||||
'space-y-1 group cursor-pointer relative',
|
||||
mobileMenuOpen ? 'w-4 h-4' : 'w-4 h-[8px]',
|
||||
].join(' ')}
|
||||
>
|
||||
<span
|
||||
className={[
|
||||
'transition-all ease-out block w-4 h-px bg-scale-900 group-hover:bg-scale-1200',
|
||||
!mobileMenuOpen ? 'w-4' : 'absolute rotate-45 top-[6px]',
|
||||
].join(' ')}
|
||||
></span>
|
||||
<span
|
||||
className={[
|
||||
'transition-all ease-out block h-px bg-scale-900 group-hover:bg-scale-1200',
|
||||
!mobileMenuOpen
|
||||
? 'w-3 group-hover:w-4'
|
||||
: 'absolute w-4 -rotate-45 top-[2px]',
|
||||
].join(' ')}
|
||||
></span>
|
||||
</div>
|
||||
</button>
|
||||
<div className={[].join(' ')}>
|
||||
<img
|
||||
src={
|
||||
menuLevel
|
||||
? levelsData[menuLevel].icon + '.svg'
|
||||
: levelsData['home'].icon + '.svg'
|
||||
}
|
||||
className={[
|
||||
'transition-all duration-200',
|
||||
mobileMenuOpen ? 'invisible w-0 h-0' : 'w-4 h-4',
|
||||
].join(' ')}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className={[
|
||||
'transition-all duration-200',
|
||||
'text-scale-1200',
|
||||
mobileMenuOpen ? 'text-xs' : 'text-sm',
|
||||
].join(' ')}
|
||||
>
|
||||
{mobileMenuOpen
|
||||
? 'Close'
|
||||
: menuLevel
|
||||
? levelsData[menuLevel].name
|
||||
: levelsData['home'].name}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={[
|
||||
'transition-all ease-out duration-200',
|
||||
'absolute left-0 right-0 h-screen',
|
||||
'px-5 pl-5 py-16',
|
||||
'top-[0px]',
|
||||
'bg-scale-200',
|
||||
mobileMenuOpen
|
||||
? 'overflow-y-auto opacity-100 left-[0px] visible'
|
||||
: 'left-[-40px] h-0 opacity-0 invisible',
|
||||
].join(' ')}
|
||||
>
|
||||
<NavigationMenu />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grow px-5 max-w-7xl mx-auto py-16">
|
||||
{children}
|
||||
|
||||
<div className="mt-32">
|
||||
<FooterHelpCallout />
|
||||
</div>
|
||||
<hr className="border-scale-400 mt-8"></hr>
|
||||
<div className="flex flex-col lg:flex-row gap-3 mt-6">
|
||||
<span className="text-xs text-scale-900">Supabase 2022</span>
|
||||
<span className="text-xs text-scale-900">—</span>
|
||||
<Link href="/handbook/contributing">
|
||||
<a className="text-xs text-scale-800 hover:underline">Contributing</a>
|
||||
</Link>
|
||||
<Link href="https://supabase.com/changelog">
|
||||
<a className="text-xs text-scale-800 hover:underline">Changelog</a>
|
||||
</Link>
|
||||
|
||||
<Link href="https://github.com/supabase/supabase/blob/master/DEVELOPERS.md">
|
||||
<a className="text-xs text-scale-800 hover:underline">Author Styleguide</a>
|
||||
</Link>
|
||||
<Link href="https://supabase.com/docs/oss">
|
||||
<a className="text-xs text-scale-800 hover:underline">Open Source</a>
|
||||
</Link>
|
||||
<Link href="https://supabase.com/docs/handbook/supasquad">
|
||||
<a className="text-xs text-scale-800 hover:underline">Supasquad</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={[
|
||||
// 'top-0 left-0',
|
||||
'hidden', // experiment hidden
|
||||
'h-full',
|
||||
'left-0',
|
||||
'right-0',
|
||||
'z-10',
|
||||
'backdrop-blur-sm backdrop-filter bg-white-1200 dark:bg-blackA-600',
|
||||
mobileMenuOpen ? 'absolute MOBILE-MENU-OPEN' : 'hidden h-0 MOBILE-MENU-CLOSED',
|
||||
].join(' ')}
|
||||
onClick={() => menuState.setMenuMobileOpen(!mobileMenuOpen)}
|
||||
></div>
|
||||
<div className={['pb-10 px-5 md:px-10 md:flex'].join(' ')}>
|
||||
<NavigationMenu />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</NavigationMenuContextProvider>
|
||||
<div
|
||||
// #docs-content-container is used by layout to scroll to top
|
||||
id="docs-content-container"
|
||||
className={[
|
||||
'w-full h-screen transition-all ease-out',
|
||||
mobileMenuOpen ? 'overflow-hidden' : 'overflow-auto',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="flex flex-col relative">
|
||||
{/* <NavigationMenu /> */}
|
||||
<div className={['lg:sticky top-0 z-10 overflow-hidden'].join(' ')}>
|
||||
<TopNavBarRef />
|
||||
</div>
|
||||
<div
|
||||
className={[
|
||||
'sticky transition-all top-0',
|
||||
'z-10',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className={['lg:hidden', 'px-5 ', 'border-b z-10'].join(' ')}>
|
||||
<div
|
||||
className={[
|
||||
'transition-all ease-out z-10',
|
||||
'top-0',
|
||||
mobileMenuOpen && 'absolute',
|
||||
'flex items-center h-[40px]',
|
||||
mobileMenuOpen ? 'gap-0' : 'gap-3',
|
||||
].join(' ')}
|
||||
>
|
||||
<button
|
||||
className={['mr-2', mobileMenuOpen && 'mt-0.5'].join(' ')}
|
||||
onClick={() => menuState.setMenuMobileOpen(!mobileMenuOpen)}
|
||||
>
|
||||
<div
|
||||
className={[
|
||||
'space-y-1 group cursor-pointer relative',
|
||||
mobileMenuOpen ? 'w-4 h-4' : 'w-4 h-[8px]',
|
||||
].join(' ')}
|
||||
>
|
||||
<span
|
||||
className={[
|
||||
'transition-all ease-out block w-4 h-px bg-scale-900 group-hover:bg-scale-1200',
|
||||
!mobileMenuOpen ? 'w-4' : 'absolute rotate-45 top-[6px]',
|
||||
].join(' ')}
|
||||
></span>
|
||||
<span
|
||||
className={[
|
||||
'transition-all ease-out block h-px bg-scale-900 group-hover:bg-scale-1200',
|
||||
!mobileMenuOpen
|
||||
? 'w-3 group-hover:w-4'
|
||||
: 'absolute w-4 -rotate-45 top-[2px]',
|
||||
].join(' ')}
|
||||
></span>
|
||||
</div>
|
||||
</button>
|
||||
<div className={[].join(' ')}>
|
||||
<img
|
||||
src={
|
||||
menuLevel
|
||||
? levelsData[menuLevel].icon + '.svg'
|
||||
: levelsData['home'].icon + '.svg'
|
||||
}
|
||||
className={[
|
||||
'transition-all duration-200',
|
||||
mobileMenuOpen ? 'invisible w-0 h-0' : 'w-4 h-4',
|
||||
].join(' ')}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className={[
|
||||
'transition-all duration-200',
|
||||
'text-scale-1200',
|
||||
mobileMenuOpen ? 'text-xs' : 'text-sm',
|
||||
].join(' ')}
|
||||
>
|
||||
{mobileMenuOpen
|
||||
? 'Close'
|
||||
: menuLevel
|
||||
? levelsData[menuLevel].name
|
||||
: levelsData['home'].name}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={[
|
||||
'transition-all ease-out duration-200',
|
||||
'absolute left-0 right-0 h-screen',
|
||||
'px-5 pl-5 py-16',
|
||||
'top-[0px]',
|
||||
'bg-scale-200',
|
||||
mobileMenuOpen
|
||||
? 'overflow-y-auto opacity-100 left-[0px] visible'
|
||||
: 'left-[-40px] h-0 opacity-0 invisible',
|
||||
].join(' ')}
|
||||
>
|
||||
<NavigationMenu />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grow px-5 max-w-7xl mx-auto py-16">
|
||||
{children}
|
||||
|
||||
<div className="mt-32">
|
||||
<FooterHelpCallout />
|
||||
</div>
|
||||
<hr className="border-scale-400 mt-8"></hr>
|
||||
<div className="flex flex-col lg:flex-row gap-3 mt-6">
|
||||
<span className="text-xs text-scale-900">Supabase 2022</span>
|
||||
<span className="text-xs text-scale-900">—</span>
|
||||
<Link href="/handbook/contributing">
|
||||
<a className="text-xs text-scale-800 hover:underline">Contributing</a>
|
||||
</Link>
|
||||
<Link href="https://supabase.com/changelog">
|
||||
<a className="text-xs text-scale-800 hover:underline">Changelog</a>
|
||||
</Link>
|
||||
|
||||
<Link href="https://github.com/supabase/supabase/blob/master/DEVELOPERS.md">
|
||||
<a className="text-xs text-scale-800 hover:underline">Author Styleguide</a>
|
||||
</Link>
|
||||
<Link href="https://supabase.com/docs/oss">
|
||||
<a className="text-xs text-scale-800 hover:underline">Open Source</a>
|
||||
</Link>
|
||||
<Link href="https://supabase.com/supasquad">
|
||||
<a className="text-xs text-scale-800 hover:underline">SupaSquad</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={[
|
||||
// 'top-0 left-0',
|
||||
'hidden', // experiment hidden
|
||||
'h-full',
|
||||
'left-0',
|
||||
'right-0',
|
||||
'z-10',
|
||||
'backdrop-blur-sm backdrop-filter bg-white-1200 dark:bg-blackA-600',
|
||||
mobileMenuOpen ? 'absolute MOBILE-MENU-OPEN' : 'hidden h-0 MOBILE-MENU-CLOSED',
|
||||
].join(' ')}
|
||||
onClick={() => menuState.setMenuMobileOpen(!mobileMenuOpen)}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
import { MDXProvider } from '@mdx-js/react'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FC, useEffect, useRef, useState } from 'react'
|
||||
import components from '~/components'
|
||||
import Footer from '~/components/Footer'
|
||||
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
|
||||
import NavBar from '~/components/Navigation/NavBar'
|
||||
import { menuItems } from '~/components/Navigation/Navigation.constants'
|
||||
import SideBar from '~/components/Navigation/SideBar'
|
||||
import { getPageType } from '~/lib/helpers'
|
||||
|
||||
interface Props {
|
||||
meta: { title: string; description?: string; hide_table_of_contents?: boolean }
|
||||
children: any
|
||||
toc?: any
|
||||
currentPage?: string
|
||||
}
|
||||
|
||||
const Layout: FC<Props> = ({ meta, children }) => {
|
||||
const { asPath } = useRouter()
|
||||
const page = getPageType(asPath)
|
||||
|
||||
useEffect(() => {
|
||||
const key = localStorage.getItem('supabaseDarkMode')
|
||||
if (!key) {
|
||||
// Default to dark mode if no preference config
|
||||
document.documentElement.className = 'dark'
|
||||
} else {
|
||||
document.documentElement.className = key === 'true' ? 'dark' : ''
|
||||
}
|
||||
}, [])
|
||||
|
||||
const articleRef = useRef()
|
||||
const [tocList, setTocList] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
const articleEl = articleRef.current as HTMLElement
|
||||
|
||||
if (!articleRef.current) return
|
||||
const headings = Array.from(articleEl.querySelectorAll('h2, h3'))
|
||||
const newHeadings = headings
|
||||
.filter((heading) => heading.id)
|
||||
.map((heading) => {
|
||||
const text = heading.textContent.replace('#', '')
|
||||
const link = heading.querySelector('a').getAttribute('href')
|
||||
const level = heading.tagName === 'H2' ? 2 : 3
|
||||
return { text, link, level }
|
||||
})
|
||||
setTocList(newHeadings)
|
||||
}, [])
|
||||
|
||||
const hasTableOfContents = tocList.length > 0
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextSeo
|
||||
title={`${meta?.title} | Supabase`}
|
||||
description={meta?.description ? meta?.description : meta?.title}
|
||||
openGraph={{
|
||||
title: meta?.title,
|
||||
description: meta?.description,
|
||||
url: `https://supabase.com/docs${asPath}`,
|
||||
images: [
|
||||
{
|
||||
url: `https://supabase.com/docs/img/supabase-og-image.png`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
|
||||
<main>
|
||||
<NavBar
|
||||
// currentPage={page}
|
||||
/>
|
||||
<div className="flex w-full flex-row">
|
||||
<SideBar menuItems={menuItems[page]} />
|
||||
<div className="main-content-pane docs-width grid md:grid-cols-12 gap-4 justify-between p-4 pb-8 w-full">
|
||||
<div
|
||||
className={`${
|
||||
meta?.hide_table_of_contents || !hasTableOfContents
|
||||
? 'col-span-12 xl:col-start-2 xl:col-span-10 2xl:col-start-3 2xl:col-span-8'
|
||||
: 'col-span-12 lg:col-span-9'
|
||||
} py-2 lg:py-4 px-2 lg:px-8 mx-auto`}
|
||||
>
|
||||
<article
|
||||
ref={articleRef}
|
||||
className={`${
|
||||
meta?.hide_table_of_contents || !hasTableOfContents ? 'xl:min-w-[880px]' : ''
|
||||
} doc-content-container prose dark:prose-dark dark:bg-scale-200 width-full mt-8 2xl:max-w-[880px]`}
|
||||
>
|
||||
{meta?.title && <h1>{meta.title}</h1>}
|
||||
<MDXProvider components={components}>{children}</MDXProvider>
|
||||
</article>
|
||||
</div>
|
||||
{hasTableOfContents && !meta?.hide_table_of_contents && (
|
||||
<div
|
||||
className={[
|
||||
'border-scale-400 dark:bg-scale-200 table-of-contents-height border-l',
|
||||
'thin-scrollbar overflow-y-auto sticky hidden xl:block md:col-span-3 px-2',
|
||||
].join(' ')}
|
||||
>
|
||||
<GuidesTableOfContents list={tocList} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout
|
||||
@@ -1,143 +0,0 @@
|
||||
import { MDXProvider } from '@mdx-js/react'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FC, useEffect, useRef, useState } from 'react'
|
||||
import components from '~/components'
|
||||
import { highlightSelectedTocItem } from '~/components/CustomHTMLElements/CustomHTMLElements.utils'
|
||||
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
|
||||
import useHash from '~/hooks/useHash'
|
||||
import { getPageType } from '~/lib/helpers'
|
||||
|
||||
interface Props {
|
||||
meta: {
|
||||
title: string
|
||||
description?: string
|
||||
hide_table_of_contents?: boolean
|
||||
breadcrumb?: string
|
||||
subtitle?: string
|
||||
}
|
||||
children: any
|
||||
toc?: any
|
||||
currentPage?: string
|
||||
hideToc?: boolean
|
||||
}
|
||||
|
||||
const Layout: FC<Props> = (props) => {
|
||||
const [hash] = useHash()
|
||||
const [active, setActive] = useState(false)
|
||||
|
||||
const articleRef = useRef()
|
||||
const [tocList, setTocList] = useState([])
|
||||
|
||||
const { asPath } = useRouter()
|
||||
const page = getPageType(asPath)
|
||||
|
||||
useEffect(() => {
|
||||
const key = localStorage.getItem('supabaseDarkMode')
|
||||
if (!key) {
|
||||
// Default to dark mode if no preference config
|
||||
document.documentElement.className = 'dark'
|
||||
} else {
|
||||
document.documentElement.className = key === 'true' ? 'dark' : ''
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (hash && tocList.length > 0) {
|
||||
highlightSelectedTocItem(hash as string)
|
||||
}
|
||||
}, [hash, JSON.stringify(tocList)])
|
||||
|
||||
useEffect(() => {
|
||||
const articleEl = articleRef.current as HTMLElement
|
||||
|
||||
if (!articleRef.current) return
|
||||
const headings = Array.from(articleEl.querySelectorAll('h2, h3'))
|
||||
const newHeadings = headings
|
||||
.filter((heading) => heading.id)
|
||||
.map((heading) => {
|
||||
const text = heading.textContent.replace('#', '')
|
||||
const link = heading.querySelector('a').getAttribute('href')
|
||||
const level = heading.tagName === 'H2' ? 2 : 3
|
||||
return { text, link, level }
|
||||
})
|
||||
setTocList(newHeadings)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(function () {
|
||||
setActive(true)
|
||||
}, 150)
|
||||
}, [])
|
||||
|
||||
const hasTableOfContents = tocList.length > 0
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextSeo
|
||||
title={`${props.meta?.title} | Supabase`}
|
||||
description={props.meta?.description ? props.meta?.description : props.meta?.title}
|
||||
openGraph={{
|
||||
title: props.meta?.title,
|
||||
description: props.meta?.description,
|
||||
url: `https://supabase.com/docs${asPath}`,
|
||||
images: [
|
||||
{
|
||||
url: `https://supabase.com/docs/img/supabase-og-image.png`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className={['grid grid-cols-12 relative'].join(' ')}>
|
||||
<div
|
||||
className={[
|
||||
'relative',
|
||||
!props.hideToc ? 'col-span-12 md:col-span-9' : 'col-span-12',
|
||||
'transition-all ease-out',
|
||||
'duration-100',
|
||||
active ? 'opacity-100 left-0' : 'opacity-0 left-6',
|
||||
].join(' ')}
|
||||
>
|
||||
{props.meta.breadcrumb && (
|
||||
<p className="text-brand-900 tracking-wider mb-3">{props.meta.breadcrumb}</p>
|
||||
)}
|
||||
<article
|
||||
ref={articleRef}
|
||||
className={`${
|
||||
props.meta?.hide_table_of_contents || !hasTableOfContents ? '' : ''
|
||||
} prose dark:prose-dark max-w-none`}
|
||||
>
|
||||
<h1 className="mb-0">{props.meta.title}</h1>
|
||||
{props.meta.subtitle && (
|
||||
<h2 className="mt-3 text-xl text-scale-1100">{props.meta.subtitle}</h2>
|
||||
)}
|
||||
<div className="max-w-xs w-32 h-[1px] bg-gradient-to-r from-brand-800 to-brand-900 my-8"></div>
|
||||
<MDXProvider components={components}>{props.children}</MDXProvider>
|
||||
</article>
|
||||
</div>
|
||||
{!props.hideToc && hasTableOfContents && !props.meta?.hide_table_of_contents && (
|
||||
<div
|
||||
className={[
|
||||
'col-span-3',
|
||||
'border-scale-400 dark:bg-scale-200 table-of-contents-height',
|
||||
'thin-scrollbar overflow-y-auto sticky hidden md:block md:col-span-3 px-2',
|
||||
'transition-all ease-out',
|
||||
'duration-100',
|
||||
active ? 'opacity-100 left-0' : 'opacity-0 left-6',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="border-l">
|
||||
<span className="block font-mono text-xs uppercase text-scale-1200 px-5 mb-6">
|
||||
On this page
|
||||
</span>
|
||||
<GuidesTableOfContents list={tocList} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout
|
||||
@@ -1,23 +1,124 @@
|
||||
import NewLayout from './current'
|
||||
import OldLayout from './archived'
|
||||
import { FC } from 'react'
|
||||
import { MDXProvider } from '@mdx-js/react'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FC, useEffect, useRef, useState } from 'react'
|
||||
import components from '~/components'
|
||||
import { highlightSelectedTocItem } from '~/components/CustomHTMLElements/CustomHTMLElements.utils'
|
||||
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
|
||||
import useHash from '~/hooks/useHash'
|
||||
import { getPageType } from '~/lib/helpers'
|
||||
|
||||
interface Props {
|
||||
meta: { title: string; description?: string; hide_table_of_contents?: boolean; video?: string }
|
||||
meta: {
|
||||
title: string
|
||||
description?: string
|
||||
hide_table_of_contents?: boolean
|
||||
breadcrumb?: string
|
||||
subtitle?: string
|
||||
}
|
||||
children: any
|
||||
toc?: any
|
||||
currentPage?: string
|
||||
hideToc?: boolean
|
||||
}
|
||||
|
||||
const Layout: FC<Props> = (props) => {
|
||||
switch (process.env.NEXT_PUBLIC_NEW_DOCS) {
|
||||
case 'true':
|
||||
return NewLayout(props)
|
||||
break
|
||||
default:
|
||||
return OldLayout(props)
|
||||
break
|
||||
}
|
||||
const [hash] = useHash()
|
||||
|
||||
const articleRef = useRef()
|
||||
const [tocList, setTocList] = useState([])
|
||||
|
||||
const { asPath } = useRouter()
|
||||
const page = getPageType(asPath)
|
||||
|
||||
useEffect(() => {
|
||||
if (hash && tocList.length > 0) {
|
||||
highlightSelectedTocItem(hash as string)
|
||||
}
|
||||
}, [hash, JSON.stringify(tocList)])
|
||||
|
||||
useEffect(() => {
|
||||
const articleEl = articleRef.current as HTMLElement
|
||||
|
||||
if (!articleRef.current) return
|
||||
const headings = Array.from(articleEl.querySelectorAll('h2, h3'))
|
||||
const newHeadings = headings
|
||||
.filter((heading) => heading.id)
|
||||
.map((heading) => {
|
||||
const text = heading.textContent.replace('#', '')
|
||||
const link = heading.querySelector('a').getAttribute('href')
|
||||
const level = heading.tagName === 'H2' ? 2 : 3
|
||||
return { text, link, level }
|
||||
})
|
||||
setTocList(newHeadings)
|
||||
}, [])
|
||||
|
||||
const hasTableOfContents = tocList.length > 0
|
||||
|
||||
return (
|
||||
<>
|
||||
<NextSeo
|
||||
title={`${props.meta?.title} | Supabase`}
|
||||
description={props.meta?.description ? props.meta?.description : props.meta?.title}
|
||||
openGraph={{
|
||||
title: props.meta?.title,
|
||||
description: props.meta?.description,
|
||||
url: `https://supabase.com/docs${asPath}`,
|
||||
images: [
|
||||
{
|
||||
url: `https://supabase.com/docs/img/supabase-og-image.png`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className={['grid grid-cols-12 relative'].join(' ')}>
|
||||
<div
|
||||
className={[
|
||||
'relative',
|
||||
!props.hideToc ? 'col-span-12 md:col-span-9' : 'col-span-12',
|
||||
'transition-all ease-out',
|
||||
'duration-100',
|
||||
].join(' ')}
|
||||
>
|
||||
{props.meta.breadcrumb && (
|
||||
<p className="text-brand-900 tracking-wider mb-3">{props.meta.breadcrumb}</p>
|
||||
)}
|
||||
<article
|
||||
ref={articleRef}
|
||||
className={`${
|
||||
props.meta?.hide_table_of_contents || !hasTableOfContents ? '' : ''
|
||||
} prose dark:prose-dark max-w-none`}
|
||||
>
|
||||
<h1 className="mb-0">{props.meta.title}</h1>
|
||||
{props.meta.subtitle && (
|
||||
<h2 className="mt-3 text-xl text-scale-1100">{props.meta.subtitle}</h2>
|
||||
)}
|
||||
<div className="max-w-xs w-32 h-[1px] bg-gradient-to-r from-brand-800 to-brand-900 my-8"></div>
|
||||
<MDXProvider components={components}>{props.children}</MDXProvider>
|
||||
</article>
|
||||
</div>
|
||||
{!props.hideToc && hasTableOfContents && !props.meta?.hide_table_of_contents && (
|
||||
<div
|
||||
className={[
|
||||
'col-span-3',
|
||||
'border-scale-400 dark:bg-scale-200 table-of-contents-height',
|
||||
'thin-scrollbar overflow-y-auto sticky hidden md:block md:col-span-3 px-2',
|
||||
'transition-all ease-out',
|
||||
'duration-100',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="border-l">
|
||||
<span className="block font-mono text-xs uppercase text-scale-1200 px-5 mb-6">
|
||||
On this page
|
||||
</span>
|
||||
<GuidesTableOfContents list={tocList} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout
|
||||
@@ -3,9 +3,9 @@ import fs from 'fs'
|
||||
import matter from 'gray-matter'
|
||||
import { serialize } from 'next-mdx-remote/serialize'
|
||||
|
||||
import { remarkCodeHike } from '@code-hike/mdx'
|
||||
// import { remarkCodeHike } from '@code-hike/mdx'
|
||||
// import codeHikeTheme from '~/codeHikeTheme.js'
|
||||
import theme from 'shiki/themes/solarized-dark.json'
|
||||
// import theme from 'shiki/themes/solarized-dark.json'
|
||||
|
||||
async function generateRefMarkdown(sections, slug) {
|
||||
let markdownContent = []
|
||||
|
||||
@@ -50,6 +50,9 @@ const nextConfig = {
|
||||
dangerouslyAllowSVG: true,
|
||||
domains: ['avatars.githubusercontent.com', 'github.com', 'user-images.githubusercontent.com'],
|
||||
},
|
||||
experimental: {
|
||||
mdxRs: true,
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -35,21 +35,15 @@
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-js": "^1.7.2",
|
||||
"@algolia/autocomplete-plugin-recent-searches": "^1.7.2",
|
||||
"@apollo/client": "^3.6.6",
|
||||
"@code-hike/mdx": "^0.7.4",
|
||||
"@docsearch/react": "^3.3.0",
|
||||
"@mdx-js/loader": "^1.6.22",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"@next/mdx": "^12.0.4",
|
||||
"@radix-ui/react-accordion": "^1.0.1",
|
||||
"@radix-ui/react-collapsible": "^1.0.1",
|
||||
"@tailwindcss/typography": "^0.4.1",
|
||||
"algoliasearch": "^4.14.2",
|
||||
"babel": "^6.23.0",
|
||||
"common": "*",
|
||||
"config": "*",
|
||||
"dotenv": "^16.0.3",
|
||||
"graphql": "^16.5.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"isbot": "^3.6.5",
|
||||
"jsrsasign": "^10.5.26",
|
||||
@@ -62,9 +56,7 @@
|
||||
"next-mdx-toc": "^0.1.3",
|
||||
"next-plugin-yaml": "^1.0.1",
|
||||
"next-seo": "^5.14.1",
|
||||
"next-themes": "0.0.15",
|
||||
"next-transpile-modules": "^9.0.0",
|
||||
"octokit": "^2.0.10",
|
||||
"react": "17.0.2",
|
||||
"react-copy-to-clipboard": "^5.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
@@ -73,17 +65,14 @@
|
||||
"react-syntax-highlighter": "^15.3.1",
|
||||
"remark": "^14.0.2",
|
||||
"remark-admonitions": "^1.2.1",
|
||||
"remark-autolink-headings": "^7.0.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-html": "^15.0.0",
|
||||
"remark-mdx": "^2.0.0-rc.2",
|
||||
"remark-prism": "^1.3.6",
|
||||
"remark-slug": "^7.0.1",
|
||||
"shiki": "^0.11.1",
|
||||
"ui": "*",
|
||||
"valtio": "^1.7.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dotenv": "^16.0.3",
|
||||
"@types/node": "^17.0.12",
|
||||
"@types/react": "17.0.39",
|
||||
"config": "*",
|
||||
|
||||
@@ -36,7 +36,6 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
* handle "scroll to top" behaviour on route change
|
||||
*/
|
||||
if (document) {
|
||||
console.log('url', url)
|
||||
// do not scroll to top for reference docs
|
||||
if (!url.includes('reference/')) {
|
||||
// scroll container div to top
|
||||
@@ -45,10 +44,8 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
if (container) container.scrollTop = 0
|
||||
}
|
||||
}
|
||||
menuState.setMenuMobileOpen(false)
|
||||
}
|
||||
|
||||
function handlePagrScroll() {}
|
||||
// Listen for page changes after a navigation or when the query changes
|
||||
router.events.on('routeChangeComplete', handleRouteChange)
|
||||
return () => {
|
||||
|
||||
@@ -109,16 +109,20 @@ Unofficial libraries [supported by the community](https://github.com/supabase-co
|
||||
|
||||
<br />
|
||||
|
||||
## File Storage
|
||||
## Storage
|
||||
|
||||
### Large File storage
|
||||
### File storage
|
||||
|
||||
Supabase Storage makes it simple to store and serve large files. [Docs](/docs/guides/storage).
|
||||
Supabase Storage makes it simple to store and serve files. [Docs](/docs/guides/storage).
|
||||
|
||||
### Storage CDN
|
||||
|
||||
Cache large files using the Supabase CDN. [Docs](/docs/guides/storage-cdn).
|
||||
|
||||
### Image Transformations
|
||||
|
||||
Transform images on the fly. [Docs](/docs/guides/storage/image-transformations).
|
||||
|
||||
<br />
|
||||
|
||||
## Edge Functions
|
||||
@@ -159,9 +163,11 @@ Both Postgres and the Supabase Platform are production-ready. Some tools we offe
|
||||
| Realtime | Postgres Changes | `GA` |
|
||||
| Realtime | Broadcast | `beta` |
|
||||
| Realtime | Presence | `beta` |
|
||||
| Storage | Backend (S3) | `GA` |
|
||||
| Storage | API | `beta` |
|
||||
| Storage | CDN | `beta` |
|
||||
| Storage | | `GA` |
|
||||
| Storage | S3 Backend | `GA` |
|
||||
| Storage | CDN | `GA` |
|
||||
| Storage | Smart CDN | `beta` |
|
||||
| Storage | Image Transformations | `beta` |
|
||||
| Edge Functions | | `beta` |
|
||||
| Auth | OAuth Providers | `beta` |
|
||||
| Auth | Passwordless | `beta` |
|
||||
|
||||
@@ -5,7 +5,8 @@ import { PhoneLoginsItems } from '~/components/Navigation/NavigationMenu/Navigat
|
||||
|
||||
export const meta = {
|
||||
title: 'Passwordless Login',
|
||||
description: 'Learn about logging in with email one-time passwords (magic links) and SMS one-time passwords.',
|
||||
description:
|
||||
'Learn about logging in with email one-time passwords (magic links) and SMS one-time passwords.',
|
||||
}
|
||||
|
||||
Passwordless login is a method of authentication that allows users to log in to a website or application without using a password. Instead of entering a password, the user may be asked to provide some other form of authentication, such as a one-time code sent via email or SMS, or by using a biometric factor like their fingerprint or facial recognition.
|
||||
@@ -32,7 +33,7 @@ a link which they can click to log in to their account) and one-time passwords v
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 gap-10 not-prose py-8">
|
||||
<Link href={`/auth/auth-magic-link`} passHref>
|
||||
<Link href={`/guides/auth/auth-magic-link`} passHref>
|
||||
<a>
|
||||
<IconPanel title={'Magic Link'} span="col-span-6" icon={
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 text-gold-1000">
|
||||
@@ -51,7 +52,7 @@ a link which they can click to log in to their account) and one-time passwords v
|
||||
### Phone SMS One-Time Password (OTP)
|
||||
|
||||
<div className="grid grid-cols-12 gap-10 not-prose py-8">
|
||||
{PhoneLoginsItems.map((item) => (
|
||||
{PhoneLoginsItems.map((item) => (
|
||||
<Link href={`/${item.url}`} key={item.name} passHref>
|
||||
<a className="col-span-4">
|
||||
<IconPanel
|
||||
|
||||
@@ -147,7 +147,7 @@ channel.on('presence', { event: 'join' }, ({ newPresences }) => {
|
||||
})
|
||||
|
||||
channel.on('presence', { event: 'leave' }, ({ leftPresences }) => {
|
||||
console.log('Users have left: ', newPresences)
|
||||
console.log('Users have left: ', leftPresences)
|
||||
})
|
||||
|
||||
channel.subscribe(async (status) => {
|
||||
|
||||
@@ -9,20 +9,63 @@ export const meta = {
|
||||
|
||||
All assets uploaded to Supabase Storage are cached on a Content Delivery Network (CDN) to improve the latency for users all around the world. CDNs are a geographically distributed set of servers or **nodes** which caches content from an **origin server**. For Supabase Storage, the origin is the storage server running in the [same region as your project](https://app.supabase.com/project/_/settings/general). Aside from performance, CDNs also help with security and availability by mitigating Distributed Denial of Service and other application attacks.
|
||||
|
||||
## Example
|
||||
## Basic CDN
|
||||
|
||||
Let’s walk through an example of how a CDN helps with performance. A new bucket is created for a Supabase project launched in Singapore. All requests to the Supabase Storage API first hit the CDN. A user from the United States requests an object and is routed to the U.S. CDN. At this point, that CDN node does not have the object in its cache and pings the origin server in Singapore. Another user, also in the United States, requests the same object and is served directly from the CDN cache in the United States instead of routing the request back to Singapore.
|
||||
Our basic CDN caches objects based on the cache time set when uploading objects.
|
||||
|
||||
## Cache duration
|
||||
### Example
|
||||
|
||||
By default, assets are cached both in the CDN and in the user's browser for 1 hour. After this, the CDN nodes ping the storage server to see if an object has been updated. You can modify this cache time when you are [uploading](/docs/reference/javascript/storage-from-upload) or [updating](/docs/reference/javascript/storage-from-update) an object by modifying the `cacheControl` parameter. If you expect the object to not change at a given URL, setting a longer cache duration is preferable.
|
||||
Let’s walk through an example of how a CDN helps with performance.
|
||||
|
||||
If you need to update the version of the object stored in the CDN, there are various cache-busting techniques you can use. The most common way to do this is to add a version query parameter in the URL. For example, you can use a URL like `/storage/v1/object/sign/profile-pictures/cat.jpg?token=eyJh...&version=1` in your applications and set a long cache time of 1 year. When you want to update the cat picture, you can increment the version query parameter in the URL. The CDN will treat `/storage/v1/object/sign/profile-pictures/cat.jpg?token=eyJh...&version=2` as a new object and pings the origin for the updated version.
|
||||
A new bucket is created for a Supabase project launched in Singapore. All requests to the Supabase Storage API are routed to the CDN first.
|
||||
|
||||
A user from the United States requests an object and is routed to the U.S. CDN. At this point, that CDN node does not have the object in its cache and pings the origin server in Singapore.
|
||||

|
||||
|
||||
Another user, also in the United States, requests the same object and is served directly from the CDN cache in the United States instead of routing the request back to Singapore.
|
||||

|
||||
|
||||
### Cache duration
|
||||
|
||||
By default, assets are cached both in the CDN and in the user's browser for 1 hour. After this, the CDN nodes ping the storage server to see if an object has been updated. You can modify this cache time when you are [uploading](/docs/reference/javascript/storage-from-upload) or [updating](/docs/reference/javascript/storage-from-update) an object by modifying the `cacheControl` parameter.
|
||||
If you expect the object to not change at a given URL, setting a longer cache duration is preferable.
|
||||
|
||||
If you need to update the version of the object stored in the CDN, there are various cache-busting techniques you can use. The most common way to do this is to add a version query parameter in the URL.
|
||||
|
||||
For example, you can use a URL like `/storage/v1/object/sign/profile-pictures/cat.jpg?token=eyJh...&version=1` in your applications and set a long cache time of 1 year.
|
||||
|
||||
When you want to update the cat picture, you can increment the version query parameter in the URL. The CDN treats `/storage/v1/object/sign/profile-pictures/cat.jpg?token=eyJh...&version=2` as a new object and pings the origin for the updated version.
|
||||
|
||||
If your asset is updated frequently, we recommend that you re-upload the new asset in a different key, this way you'll always have the latest changes available immediately.
|
||||
|
||||
Note that CDNs might still evict your object from their cache if it has not been requested for a while from a specific region. For example, if no user from United States requests your object, it will be removed from the CDN cache even if you set a very long cache control duration.
|
||||
|
||||
The cache status of a particular request is sent in the `cf-cache-status` header. A cache status of `MISS` indicates that the CDN node did not have the object in its cache and had to ping the origin to get it. A cache status of `HIT` indicates that the object was sent directly from the CDN.
|
||||
|
||||
|
||||
## Smart CDN Caching
|
||||
|
||||
<Admonition type="note">
|
||||
Smart CDN caching is automatically enabled for [Pro tiers and
|
||||
above](https://supabase.com/pricing).
|
||||
</Admonition>
|
||||
|
||||
With Smart CDN caching enabled, the asset metadata in your database is synchronized to the edge. This automatically revalidates the cache when the asset is changed or deleted.
|
||||
|
||||
Additionally, the smart CDN has a higher cache hit ratio as the origin server is shielded from asset requests that haven't changed when using different query strings in the URL.
|
||||
|
||||
### Cache duration
|
||||
|
||||
When Smart CDN is enabled, the asset is cached on the CDN for as long as possible. You can still control how long assets are stored in the browser using the [cacheControl](/docs/reference/javascript/storage-from-upload) option when uploading a file. Smart CDN caching works with all types of storage operations including signed URLs.
|
||||
|
||||
When a file is updated or deleted, the CDN cache is automatically invalidated to reflect the change (including transformed images).
|
||||
It can take **up to 60 seconds** for the CDN cache to be invalidated as the asset metadata has to propagate across all the data-centers around the globe.
|
||||
|
||||
When an asset is invalidated at the CDN level, browsers may not update its cache.
|
||||
|
||||
If your asset is updated frequently, we recommend that you re-upload the new asset in a different key, this way you'll always have the latest changes available without waiting the propagation delay.
|
||||
If you expect your asset to be deleted, we recommend setting a low browser TTL value using the `cacheControl` option when using smart CDN caching, the default is 1 hour which generally is a good default.
|
||||
|
||||
## Public vs Private Buckets
|
||||
|
||||
Objects in public buckets do not require any Authorization to access objects. This leads to a better cache hit rate compared to private buckets. For private buckets, permissions for accessing each object is checked on a per user level. For example, if two different users access the same object in a private bucket from the same region, it results in a cache miss for both the users since they might have different security policies attached to them. On the other hand, if two different users access the same object in a public bucket from the same region, it results in a cache hit for the second user.
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'storage-image-transformations',
|
||||
title: 'Storage Image Transformations',
|
||||
description: 'Transform images with Storage',
|
||||
sidebar_label: 'Image Transformations',
|
||||
}
|
||||
|
||||
Supabase Storage offers the functionality to transform and resize images dynamically. Any image stored in your buckets can be transformed and optimized for fast delivery.
|
||||
|
||||
<Admonition type="note">
|
||||
Image Resizing is currently in beta and enabled for [Pro tier and
|
||||
above](https://supabase.com/pricing).
|
||||
</Admonition>
|
||||
|
||||
## Get a public URL for a transformed image
|
||||
|
||||
You can pass a `transform` option to the functions you are currently using to interact with your objects. This returns the public URL that serves the resized image.
|
||||
|
||||
```ts
|
||||
supabase.storage.from('bucket').getPublicUrl('image.jpg', {
|
||||
transform: {
|
||||
width: 500,
|
||||
height: 600,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
An example URL could look like this:
|
||||
|
||||
`https://project_id.supabase.co/storage/v1/render/image/public/bucket/image.jpg?width=500&height=600`
|
||||
|
||||
## Signing URLs with transformation options
|
||||
|
||||
To share a transformed image for a fixed amount of time, provide the transform options when you create the signed URL:
|
||||
|
||||
```ts
|
||||
supabase.storage.from('bucket').createSignedUrl('image.jpg', 60000, {
|
||||
transform: {
|
||||
width: 200,
|
||||
height: 200,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The transformation options are embedded into the token—they cannot be changed once signed.
|
||||
|
||||
## Downloading images
|
||||
|
||||
To download a transformed image, pass the `transform` option to the `download` function.
|
||||
|
||||
```ts
|
||||
supabase.storage.from('bucket').download('image.jpg', {
|
||||
transform: {
|
||||
width: 800,
|
||||
height: 300,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## NextJS Loader
|
||||
|
||||
You can use Supabase Image Transformation to optimise your NextJS images using a custom [Loader](https://nextjs.org/docs/api-reference/next/image#loader-configuration).
|
||||
|
||||
To get started, create a `supabase-image-loader.js` file in your NextJS project which exports a default function:
|
||||
|
||||
```ts
|
||||
const projectId = '' // your supabase project id
|
||||
|
||||
export default function supabaseLoader({ src, width, height, quality }) {
|
||||
return `https://${projectId}.supabase.co/storage/v1/${src}?width=${width}&height=${
|
||||
height || 0
|
||||
}&quality=${quality || 75}`
|
||||
}
|
||||
```
|
||||
|
||||
In your `nextjs.config.js` file add the following configuration to instruct NextJS to use our custom loader
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
images: {
|
||||
loader: 'custom',
|
||||
loaderFile: './supabase-image-loader.js',
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
At this point you are ready to use the `Image` component provided by NextJS
|
||||
|
||||
```tsx
|
||||
import Image from 'next/image'
|
||||
|
||||
const MyImage = (props) => {
|
||||
return <Image src="bucket/image.png" alt="Picture of the author" width={500} height={500} />
|
||||
}
|
||||
```
|
||||
|
||||
## Transformation options
|
||||
|
||||
We currently support a few transformation options focusing on resizing and cropping images.
|
||||
|
||||
### Resizing
|
||||
|
||||
You can use `width` and `height` parameters to resize an image to a specific dimension. If only one parameter is specified, the image will be resized and cropped, maintaining the aspect ratio.
|
||||
|
||||
### Modes
|
||||
|
||||
You can use different resizing modes to fit your needs, each of them uses a different approach to resize the image:
|
||||
|
||||
Use the `resize` parameter with one of the following values:
|
||||
|
||||
- `cover` : resizes the image while keeping the aspect ratio to fill a given size and crops projecting parts. (default)
|
||||
|
||||
- `contain` : resizes the image while keeping the aspect ratio to fit a given size.
|
||||
|
||||
- `fill` : resizes the image without keeping the aspect ratio.
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
supabase.storage.from('bucket').download('image.jpg', {
|
||||
transform: {
|
||||
width: 800,
|
||||
height: 300,
|
||||
resize: 'contain', // 'cover' | 'fill'
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Limits
|
||||
|
||||
- Width and height must be an integer value between 1-2500.
|
||||
- The image size cannot exceed 25MB.
|
||||
- The image resolution cannot exceed 50MP.
|
||||
|
||||
### Supported Image Formats
|
||||
|
||||
| Format | Extension | Source | Result |
|
||||
| ------ | --------- | ------ | ------ |
|
||||
| PNG | `png` | ☑️ | ☑️ |
|
||||
| JPEG | `jpg` | ☑️ | ☑️ |
|
||||
| WebP | `webp` | ☑️ | ☑️ |
|
||||
| AVIF | `avif` | ☑️ | ☑️ |
|
||||
| GIF | `gif` | ☑️ | ☑️ |
|
||||
| ICO | `ico` | ☑️ | ☑️ |
|
||||
| SVG | `svg` | ☑️ | ☑️ |
|
||||
| HEIC | `heic` | ☑️ | ❌ |
|
||||
| BMP | `bmp` | ☑️ | ☑️ |
|
||||
| TIFF | `tiff` | ☑️ | ☑️ |
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -11,8 +11,10 @@ export const meta = {
|
||||
|
||||
<h2 className="text-base max-w-xl m-0">
|
||||
Supabase is an open source Firebase alternative providing all the backend features you need to
|
||||
build a product. Choose a quickstart for an overview or dive straight into the different products
|
||||
and APIs.
|
||||
build a product.
|
||||
|
||||
[Learn more](/docs/architecture) about Supabase, start with the
|
||||
[tutorials](/docs/guides/getting-started#tutorials), or dive straight into the different products and APIs.
|
||||
</h2>
|
||||
|
||||
{/* start container */}
|
||||
|
||||
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 713 KiB |
@@ -73,7 +73,7 @@
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/handbook/supasquad</loc>
|
||||
<loc>https://supabase.com/supasquad</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
@@ -126,6 +126,12 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/realtime</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -234,6 +240,12 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/index</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/new/auth</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -252,36 +264,6 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/index</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/company/aup</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/company/privacy</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/company/sla</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/company/terms</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/learn/auth-deep-dive/auth-deep-dive-jwts</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -313,13 +295,7 @@
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/cli/local-development</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/cli/managing-environments</loc>
|
||||
<loc>https://supabase.com/docs/guides/api/generating-types</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
@@ -510,6 +486,66 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/cli/local-development</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/cli/managing-environments</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/auth</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/best-practices</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/cicd-workflow</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/quickstart</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/architecture</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/features</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/hosting/docker</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/hosting/overview</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/arrays</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -600,138 +636,6 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/auth</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/best-practices</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/cicd-workflow</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/quickstart</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/architecture</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/features</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/hosting/docker</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/hosting/overview</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/migrations/firebase-auth</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/migrations/firebase-storage</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/migrations/firestore-data</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/migrations/heroku</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/compute-add-ons</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/custom-domains</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/database-usage</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/going-into-prod</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/logs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/metrics</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/migrating-and-upgrading-projects</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/performance</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/permissions</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/integrations/appsmith</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -871,7 +775,19 @@
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/api/generating-types</loc>
|
||||
<loc>https://supabase.com/docs/guides/realtime/postgres-changes</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/realtime/quickstart</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/realtime/rate-limits</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
@@ -901,7 +817,79 @@
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/new/auth/auth-login</loc>
|
||||
<loc>https://supabase.com/docs/guides/migrations/firebase-auth</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/migrations/firebase-storage</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/migrations/firestore-data</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/migrations/heroku</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/compute-add-ons</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/custom-domains</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/database-usage</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/going-into-prod</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/logs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/metrics</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/migrating-and-upgrading-projects</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/performance</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/permissions</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
@@ -936,48 +924,6 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/realtime/postgres-changes</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/realtime/quickstart</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/realtime/rate-limits</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/passwordless-login/email-otp-magic-link</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/passwordless-login/phone-sms-otp-messagebird</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/passwordless-login/phone-sms-otp-twilio</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/passwordless-login/phone-sms-otp-vonage</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/auth-ui</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -1008,6 +954,114 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/examples/og-image</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/quickstarts/nextjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/quickstarts/nuxtjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/quickstarts/reactjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-angular</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-expo</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-flutter</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-ionic-angular</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-ionic-react</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-ionic-vue</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-nextjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-nuxt-3</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-react</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-redwoodjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-solidjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-svelte</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-sveltekit</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-vue-3</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/http</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -1045,7 +1099,73 @@
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/examples/og-image</loc>
|
||||
<loc>https://supabase.com/docs/new/auth/auth-login</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/firebase-auth</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/firebase-data</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/firebase-storage</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/heroku</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/self-hosting/docker</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/passwordless-login/email-otp-magic-link</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/passwordless-login/phone-sms-otp-messagebird</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/passwordless-login/phone-sms-otp-twilio</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/passwordless-login/phone-sms-otp-vonage</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/supabase-cli/local-development</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/supabase-cli/managing-enviroments</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
@@ -1158,150 +1278,6 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/quickstarts/nextjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/quickstarts/nuxtjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/quickstarts/reactjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-angular</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-expo</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-flutter</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-ionic-angular</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-ionic-react</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-ionic-vue</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-nextjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-nuxt-3</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-react</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-redwoodjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-solidjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-svelte</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-sveltekit</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/getting-started/tutorials/with-vue-3</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/self-hosting/docker</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/supabase-cli/local-development</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/supabase-cli/managing-enviroments</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/firebase-auth</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/firebase-data</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/firebase-storage</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/heroku</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/api</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -1416,6 +1392,18 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/auth-admin-mfa-deletefactor</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/auth-admin-mfa-listfactors</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/auth-admin-resetpasswordforemail</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -1428,6 +1416,12 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/auth-api</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/auth-getsession</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -1440,6 +1434,44 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/auth-mfa-challenge</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/auth-mfa-challengeandverify</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/auth-mfa-enroll</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc
|
||||
>https://supabase.com/docs/reference/javascript/auth-mfa-getauthenticatorassurancelevel</loc
|
||||
>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/auth-mfa-unenroll</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/auth-mfa-verify</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/auth-onauthstatechange</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
|
||||
@@ -110,8 +110,8 @@ Today the Community are releasing both the Flutter and Dart libraries in Beta (w
|
||||
|
||||
### Get involved
|
||||
|
||||
- Help [write](/docs/handbook/supasquad#author) the Flutter Docs.
|
||||
- Help [maintain](/docs/handbook/supasquad#maintainer) the [Flutter](https://github.com/supabase/supabase-flutter/)
|
||||
- Help [write](/supasquad#author) the Flutter Docs.
|
||||
- Help [maintain](/supasquad#maintainer) the [Flutter](https://github.com/supabase/supabase-flutter/)
|
||||
and [Dart](https://github.com/supabase/supabase-dart/) libraries.
|
||||
|
||||
## Supabase Discord
|
||||
|
||||
@@ -81,7 +81,7 @@ The beauty of building in the open is that the community can get involved in add
|
||||
|
||||
## SupaSquad updates
|
||||
|
||||
The [SupaSquad](/docs/handbook/supasquad) is an official Supabase advocate program where community members help build and manage the Supabase community.
|
||||
The [SupaSquad](/supasquad) is an official Supabase advocate program where community members help build and manage the Supabase community.
|
||||
|
||||
Among many other awesome things, the SupaSquad has been absolutely on fire building [client libraries](/docs/reference/javascript/installing) for all flavors of backends and environments.
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ As always, it was challenging to pick the winners from all the great submissions
|
||||
|
||||
[Swappy.one](https://github.com/zernonia/swappy-one) - by [@zernonia](https://twitter.com/zernonia)
|
||||
|
||||
Zernonia is one of our [SupaSquad](https://supabase.com/docs/handbook/supasquad) members and continuously blows our minds with his creativity for the open-source projects he creates. For example, he's also the mastermind behind [madewithsupabase.com](https://www.madewithsupabase.com/), one of our favourite resources to see what y'all are building.
|
||||
Zernonia is one of our [SupaSquad](https://supabase.com/supasquad) members and continuously blows our minds with his creativity for the open-source projects he creates. For example, he's also the mastermind behind [madewithsupabase.com](https://www.madewithsupabase.com/), one of our favourite resources to see what y'all are building.
|
||||
|
||||
Now he can add a gold tee to his collection, and we're starting to run out of swag ideas to show our appreciation! (Send us some ideas on [Twitter](https://twitter.com/supabase)!)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Supabase Launch Week was born out of [YC Demo Day](https://www.ycombinator.com/d
|
||||
With Launch Week, we replicate Demo Day conditions, but for a whole week! This is the secret formula that works for us:
|
||||
|
||||
- The Friday before, we publish a blog post about a topic of interest (e.g. [How we launch at Supabase](https://supabase.com/blog/supabase-how-we-launch), or this time around we [explore whether you should open source your company](https://supabase.com/blog/should-i-open-source-my-company) — hint: we think you most likely should!) as well as an [overview blogpost](https://supabase.com/blog/supabase-launch-week-four) with tons of memes and hints to what we’re shipping each day.
|
||||
- Monday is Community Day, where we rally many of the awesome contributors and partners together and showcase awesome things around Supabase and open-source. This is also the time to watch out for the [#SupaLaunchWeek hashtag on Twitter](https://twitter.com/hashtag/SupaLaunchWeek?src=hashtag_click) as we arm our guest speakers, [Angels](https://supabase.com/blog/angels-of-supabase), and [SupaSquad](https://supabase.com/docs/handbook/supasquad) with free swag codes.
|
||||
- Monday is Community Day, where we rally many of the awesome contributors and partners together and showcase awesome things around Supabase and open-source. This is also the time to watch out for the [#SupaLaunchWeek hashtag on Twitter](https://twitter.com/hashtag/SupaLaunchWeek?src=hashtag_click) as we arm our guest speakers, [Angels](https://supabase.com/blog/angels-of-supabase), and [SupaSquad](https://supabase.com/supasquad) with free swag codes.
|
||||
- Tuesday, Wednesday, and Thursday are the big feature launches.
|
||||
- Friday has become the feature kitchen sink, as we ship too many things to fit into one week. It’s our “One more thing(s)” day.
|
||||
- And last but not least, coming out of Launch Week, we roll into a 10 day virtual hackathon with the community submitting tons of awesome projects to [madewithsupabase.com](https://www.madewithsupabase.com/), which, once upon a time, was a hackathon project itself. I know, quite the inception kinda stuff - so cool!
|
||||
@@ -200,7 +200,7 @@ Can’t make the event? Chuck your name on the [mailing list](https://jonmeyers.
|
||||
|
||||
After a flood of positive feedback on Jon’s “[Build a SaaS product with Next.js, Supabase and Stripe](https://egghead.io/courses/build-a-saas-product-with-next-js-supabase-and-stripe-61f2bc20)” [Egghead.io](http://Egghead.io) course, we’re doubling down with Egghead this year, partnering with them to build more tailored pathways for learning Supabase.
|
||||
|
||||
We’ll be working with our fabulous [SupaSquad](https://supabase.com/docs/handbook/supasquad) to identify the right instructors for all the different topics we’re looking to cover. If you’re interested in becoming an instructor for Supabase on Egghead, fill in the [form to join](https://supabase.com/docs/handbook/supasquad#how-to-join) the SupaSquad.
|
||||
We’ll be working with our fabulous [SupaSquad](https://supabase.com/supasquad) to identify the right instructors for all the different topics we’re looking to cover. If you’re interested in becoming an instructor for Supabase on Egghead, fill in the [form to join](https://supabase.com/supasquad#how-to-join) the SupaSquad.
|
||||
|
||||
Also, do let us know what topics you’d like to learn about [on Twitter](https://twitter.com/supabase).
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ Remix Auth Helpers are in the works. You can follow the development of this on o
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributors are welcome! You can help by creating pull requests, reporting bugs, writing documentation, replying to issues on our GitHub issue tracker, and helping others with questions they may have about using the library. If you’re already an active contributor to Supabase or any associated open source projects, consider applying to join the [SupaSquad](https://supabase.com/docs/handbook/supasquad).
|
||||
Contributors are welcome! You can help by creating pull requests, reporting bugs, writing documentation, replying to issues on our GitHub issue tracker, and helping others with questions they may have about using the library. If you’re already an active contributor to Supabase or any associated open source projects, consider applying to join the [SupaSquad](https://supabase.com/supasquad).
|
||||
|
||||
> Big shoutout to [Willow aka GHOST](https://twitter.com/onlyspaceghost) for her help with the Svelte/SvelteKit auth helper libraries.
|
||||
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
---
|
||||
title: 'Supabase Storage v2: Image resizing and Smart CDN'
|
||||
description: "We're introducing new features for Supabase Storage: Image resizing and a Smart CDN."
|
||||
author: fabrizio,inian
|
||||
image: lw6-storage/Image-resizing.jpg
|
||||
thumb: lw6-storage/Image-resizing.jpg
|
||||
tags:
|
||||
- launch-week
|
||||
date: '2022-12-13'
|
||||
toc_depth: 3
|
||||
youtubeHero: https://www.youtube-nocookie.com/embed/NpEl20iuOtg
|
||||
---
|
||||
|
||||
Today we're introducing three new features for Supabase Storage: Image resizing, webhooks, and a Smart CDN.
|
||||
|
||||
These features are designed to work together to deliver a next-gen image resizing system. Let's explore how it works.
|
||||
|
||||
<div className="bg-gray-300 rounded-lg p-6 italic">
|
||||
|
||||
Image Resizing is in Beta. While in Beta, it is only enabled for Pro-tier and Enterprise customers.
|
||||
|
||||
</div>
|
||||
|
||||
## Image resizing
|
||||
|
||||
Image resizing has been a popular request since we released Supabase Storage. For good reason - if you're providing an image upload function to your users, you can't expect them to optimize the images before uploading.
|
||||
|
||||
Supabase developers have been hacking together all sorts of workarounds using client-side compression, or external providers like Cloudinary and Imgix.
|
||||
|
||||
Today's release changes that. Resizing is now as easy as adding a few lines of code:
|
||||
|
||||
```js
|
||||
supabase.storage.from('bucket').getPublicUrl('image.jpg', {
|
||||
transform: {
|
||||
width: 500,
|
||||
height: 600,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Choosing a resizer
|
||||
|
||||
Supabase Storage uses [imgproxy](https://github.com/imgproxy/imgproxy). As usual, we assessed various open source tools available, including [Imaginary](https://github.com/h2non/imaginary) (MIT) and [Sharp](https://github.com/lovell/sharp) (Apache2).
|
||||
|
||||
All of these have very friendly open source licenses for self-hosting, so ultimately it came down to features and performance.
|
||||
|
||||
While Imaginary doesn't support some modern image formats yet (like AVIF), Sharp and imgproxy are both very feature-complete and well-maintained
|
||||
|
||||
In a head-to-head benchmark, we found the performance to be almost identical, with the deciding factor instead coming down to resources.
|
||||
|
||||
### Developer Experience
|
||||
|
||||
We spent a long time considering what a good developer experience would be for image resizing.
|
||||
|
||||

|
||||
|
||||
We landed on a simple, on-demand interface. Downloading a resized image in JavaScript looks like this:
|
||||
|
||||
```jsx
|
||||
supabase.storage.from('bucket').download('image.jpg', {
|
||||
transform: {
|
||||
width: 800,
|
||||
height: 300,
|
||||
resize: 'contain', // 'contain' | 'cover' | 'fill'
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Or you can make a direct request to the Storage API (see `width` and `height` params):
|
||||
|
||||
```bash
|
||||
GET https://project_id.supabase.co/storage/v1/render/image/public/bucket/image.jpg?width=500&height=600
|
||||
```
|
||||
|
||||
If you've never used a resizing service before, you might be interested to learn that there are various approaches. Some services require you to resize your images “server side” before you can download them in the browser. Other services allow you to resize directly from the client. We opted for this client-based approach. It feels more developer friendly.
|
||||
|
||||
We might _additionally_ launch server-side presets as we add transformations because they provide a good DX for chaining multiple transformations (resize, rotate, and watermark for example). With server-side presets, transformations can be bundled together into a single command - `?preset=profile` instead of adding `?width=200&rotate=90&watermark=image` to all your image URLs.
|
||||
|
||||
Behind the scenes, we've provided a few small niceties:
|
||||
|
||||
- **No footguns:** an image will never be resized bigger than original dimensions (even if the user specifies larger dimensions)
|
||||
- **Sane defaults:** default mode maintains aspect ratio and the resize mode is `cover`
|
||||
- **Standardization**: resize modes mirror the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) property of CSS. We support the contain, cover and fill modes.
|
||||
|
||||
## Smart CDN
|
||||
|
||||
Resizing images can get expensive, so before this release we knew we needed a better caching system. We already provide a “basic” CDN for all projects (including Free-tier), so the important update here is some smarter cache invalidation whenever there are changes.
|
||||
|
||||

|
||||
|
||||
Our Basic CDN works by adding a 1-hour cache header to every request. After this header expires, we re-check the origin (S3), to see if an image is still available. This parameter is configurable (for example, you can make it one day, or one year).
|
||||
|
||||
This approach introduces the possibility of a “stale” image. If you update an image in S3, users will continue to see the old, stale image for one hour. Similarly for deleted images - the cache will continue serving the deleted image until the cache expires.
|
||||
|
||||
With this release we're introducing a Smart CDN. Whenever you update or delete an image, the cache will be busted within 60 seconds - for every region in the world.
|
||||
|
||||
As a developer, you no longer need to worry about adjusting your cache parameters - the cache “just works”.
|
||||
|
||||
If you're interested in _how_ the cache works, see the deep-dive later in this blog post.
|
||||
|
||||
## Webhooks + Rate-limiting
|
||||
|
||||
As a “pre-release”, we've added webhooks and rate-limiting to the Storage API. We haven't exposed these parameters on our platform yet, but they are coming soon.
|
||||
|
||||
If you're self-hosting then you can use them today. It's as simple as enabling a few environment variables.
|
||||
|
||||
The webhook events we added are:
|
||||
|
||||
```bash
|
||||
ObjectCreated:Post # A new object is created
|
||||
ObjectCreated:Put # A new object is updated
|
||||
ObjectCreated:Copy # An object is duplicated
|
||||
ObjectCreated:Move # An object is created as part of a move operation
|
||||
ObjectRemoved:Delete # An object is deleted
|
||||
ObjectRemoved:Move # An object is deleted as part of a move operation
|
||||
ObjectUpdated:Metadata # An object's metadata is updated
|
||||
```
|
||||
|
||||
## Smart CDN Deep Dive
|
||||
|
||||
Let's put everything together to see how the new Smart CDN works with Imgproxy.
|
||||
|
||||
Some developers have a high “resize” workload, and some have a high “read” workload. This means that there are different scaling requirements for every developer. As a result, we kept the image resizer and the Storage API as two separate containers. This allows them to scale independently depending on your workload.
|
||||
|
||||
Let's step through a few different flows to see how it all works:
|
||||
|
||||
- Cache miss: the image has never been resized
|
||||
- Cache hit: the image has been resized and is stored in the cache
|
||||
- Cache refresh: an image is updated/deleted and the CDN needs to be updated
|
||||
|
||||
### Cache miss
|
||||
|
||||
Let's imagine one of your users makes a request for a resized cat (kitten?) image in your application. Here's the flow:
|
||||
|
||||

|
||||
|
||||
1. The request hits our globally-distributed API Gateway. This gateway reads the image metadata from a global KV store (this metadata is created when the image was uploaded).
|
||||
2. Inside the image metadata is a Cache Key - this is comprised of an [Etag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) + tenant ID + bucketname + image name. The Gateway tries to find a cached object in our CDN using the Cache Key. If the image has never been accessed, it is a “Cache miss”.
|
||||
3. The request falls back to the Storage server. Because this is a resize request, it is forwards a short-lived S3 URL to Imgproxy.
|
||||
4. Imgproxy resizes the image and returns it to the Storage Server which in then returns it to the API Gateway, with stores it in the Cache (which means that the next time the Etag is checked, the result will return directly from the cache).
|
||||
|
||||
### Cache hit
|
||||
|
||||
The cat image goes viral, and so now multiple users want to see the resized image.
|
||||
|
||||

|
||||
|
||||
1. The request hits our globally-distributed API Gateway. This gateway reads the image metadata from a global KV store.
|
||||
2. Using the Cache Key the Gateway tries to find a cached object in our CDN. Because this image has already been resized, it returns it directly from the cache.
|
||||
|
||||
### Cache refresh
|
||||
|
||||
To complete this architecture, we need to keep the global KV store (which stores the image metadata) synchronized whenever an image is changed. To avoid coupling any CDN-specific logic into the Storage server, we added Webhooks that publish events for every object created, updated and deleted.
|
||||
|
||||
Using our cat picture from above:
|
||||
|
||||

|
||||
|
||||
1. You update the cat picture to a dog picture. This triggers an `ObjectCreated:Put` event from the Supabase Storage server.
|
||||
2. A cache manager service subscribes to these events and updates the global metadata store with the new Etag. This takes around 60 seconds to propagate globally.
|
||||
3. The next time there is a request for the resized image, it will be a cache miss.
|
||||
|
||||
Most CDNs offer this functionality “natively”. However there is usually a hard-limit to the number of daily cache purges. This implementation gives us greater control over when the cache is cleared.
|
||||
|
||||
### The impact
|
||||
|
||||
Besides solving issues with stale data, one of the biggest benefits this architecture offers is higher cache hits. Why is that important? Because image resizing is slow. Every cache miss is increased latency for your users.
|
||||
|
||||

|
||||
|
||||
The green line here shows the number of cache hits to our CDN. As you can see, when we enabled the Smart CDN, the number of requests cache hits increased significantly (which means faster cat images).
|
||||
|
||||
## Availability and Pricing
|
||||
|
||||
Image Resizing is in Beta. While in Beta, it is only enabled for Pro-tier and Enterprise customers.
|
||||
|
||||
When choosing a pricing model there were a few options that competitors offered:
|
||||
|
||||
- Pay for every transformation, but don't pay for a CDN hit
|
||||
- Don't pay for transformations, but pay for every request (even if it's a CDN hit)
|
||||
|
||||
We tried to find a middle ground, where you pay for the number of images you want to transform rather than the number of transformations:
|
||||
|
||||
- You can transform 100 images per project, at no additional cost. Beyond this, it is $5 per additional 1000 images transformed.
|
||||
- Each image can be transformed up to 20 times. This is based on fair-usage. If you exceed the limit, we won't return an error and your site will continue to function (but you will get a sternly-worded email from [@AntWilson](https://twitter.com/AntWilson)).
|
||||
|
||||
Of course, all of this functionality is free in the self-hosting setup too. You can self-host the Storage API either as a standalone service or as part of the Supabase stack.
|
||||
|
||||
For local development, this is available in our CLI from v1.23.0.
|
||||
|
||||
Upgrade to supabase-js v2.2.0.
|
||||
|
||||
## Coming soon
|
||||
|
||||
A few features to look out for in the future:
|
||||
|
||||
- **Webhooks**: we'll expose all Storage webhooks and events so that you can consume them from your own services.
|
||||
- **More Transformations**: quality, rotate, blur? Cast your vote on [this GitHub Discussion](https://github.com/supabase/supabase/discussions/10918).
|
||||
- **Integrations**: Native integration with [next/image](https://nextjs.org/docs/api-reference/next/image) and [Edge Functions](https://supabase.com/edge-functions) for advanced transformations.
|
||||
|
||||
## Getting started
|
||||
|
||||
[Upgrade to Pro](https://supabase.com/pricing) to get started with Image Resizing today.
|
||||
|
||||
- Check out the Docs for [Image Resizing](https://supabase.com/docs/guides/storage/image-transformations).
|
||||
- Check out the Docs for the [Storage CDN](https://supabase.com/docs/guides/storage/cdn).
|
||||
- Get Started today with the [JavaScript](https://supabase.com/docs/reference/javascript/storage-from-download) and [Dart](https://supabase.com/docs/reference/dart/storage-from-list) client libraries.
|
||||
@@ -20,16 +20,31 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Day 2",
|
||||
"shipped": false,
|
||||
"title": "Image Processing: Resize V2 + CDN",
|
||||
"shipped": true,
|
||||
"date": "13 Dec 2022",
|
||||
"description": "Day 2",
|
||||
"description": "Image Transformations",
|
||||
"d": 2,
|
||||
"dd": "Tuesday",
|
||||
"youtube_id": "iqZlPtl_b-I",
|
||||
"blogpost": "https://supabase.com/blog",
|
||||
"docs": "https://supabase.com/docs",
|
||||
"steps": []
|
||||
"steps": [
|
||||
{
|
||||
"title": "Image Transformations",
|
||||
"blog": "/blog/storage-image-resizing-smart-cdn",
|
||||
"docs": "/docs/guides/storage/image-transformations",
|
||||
"isNew": true,
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"title": "Smart CDN",
|
||||
"description": "Faster asset delivery, now even faster.",
|
||||
"blog": "/blog/storage-image-resizing-smart-cdn",
|
||||
"docs": "/docs/guides/storage/cdn",
|
||||
"isNew": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Day 3",
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
},
|
||||
{
|
||||
"text": "SupaSquad",
|
||||
"url": "/docs/handbook/supasquad"
|
||||
"url": "/supasquad"
|
||||
},
|
||||
{
|
||||
"text": "DevTo",
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
"icon": "M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4",
|
||||
"features": [
|
||||
{
|
||||
"title": "Storage total",
|
||||
"title": "Storage",
|
||||
"tiers": {
|
||||
"free": "Up to 1 GB",
|
||||
"pro": "100 GB included, then $0.021 per GB",
|
||||
@@ -191,6 +191,14 @@
|
||||
"pro": "Up to 5GB",
|
||||
"enterprise": "Unlimited"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Asset transformations",
|
||||
"tiers": {
|
||||
"free": false,
|
||||
"pro": "Unlimited transformations for 100 origin images, then 5$ per 1000 origin images",
|
||||
"enterprise": "Unlimited"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -243,5 +243,12 @@
|
||||
"position": "Engineering",
|
||||
"author_url": "https://github.com/mildtomato",
|
||||
"author_image_url": "https://github.com/mildtomato.png"
|
||||
},
|
||||
{
|
||||
"author_id": "fabrizio",
|
||||
"author": "Fabrizio Fenoglio",
|
||||
"position": "Engineering",
|
||||
"author_url": "https://github.com/fenos",
|
||||
"author_image_url": "https://github.com/fenos.png"
|
||||
}
|
||||
]
|
||||
@@ -1600,4 +1600,9 @@ module.exports = [
|
||||
source: '/docs/reference',
|
||||
destination: '/docs#reference-documentation',
|
||||
},
|
||||
{
|
||||
permanent: true,
|
||||
source: '/docs/handbook/supasquad',
|
||||
destination: '/supasquad',
|
||||
},
|
||||
]
|
||||
@@ -38,7 +38,7 @@ export default function launchweek() {
|
||||
const { isDarkMode } = useTheme()
|
||||
const title = 'Launch Week 6'
|
||||
const description = 'Supabase Launch Week 6 | 12-18 Dec 2022'
|
||||
const liveDay = 'Monday'
|
||||
const liveDay = 'Tuesday'
|
||||
|
||||
const [supabase] = useState(() =>
|
||||
createClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!)
|
||||
@@ -102,7 +102,7 @@ export default function launchweek() {
|
||||
<Badge
|
||||
className={`!bg-transparent !py-1 !px-4 text-transparent bg-clip-text bg-gradient-to-r from-[#99bbab] to-[#396f55] !border-[#4d898c] dark:from-white dark:to-[#DFFFF1] dark:!border-[#DFFFF1] h-fit relative ${
|
||||
shipped
|
||||
? 'after:absolute after:rounded-full text-black after:bg-white after:w-full after:h-full after:top-0 after:right-0 after:bottom-0 after:left-0 after:bg-gradient-to-br after:from-[#dceef0] after:to-[#b6b6b6] dark:text-transparent dark:border-[#1f3738] after:dark:from-[#14292c] after:dark:to-[#141516] after:border-[#c8d8d9] after:dark:border-[#1f3536] after:-z-10 border-[#DFFFF1]'
|
||||
? 'after:absolute after:rounded-full text-black after:bg-white after:w-full after:h-full after:top-0 after:right-0 after:bottom-0 after:left-0 after:bg-gradient-to-br after:from-[#dceef0] after:to-[#b6b6b6] dark:text-transparent dark:!border-[#163837] after:dark:from-[#14292c] after:dark:to-[#141516] after:border-[#c8d8d9] after:dark:border-[#1f3536] after:-z-10 border-[#DFFFF1]'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
@@ -224,10 +224,10 @@ export default function launchweek() {
|
||||
<div className="relative flex items-center mb-4 sm:mb-0">
|
||||
<div className="flex">
|
||||
<Image
|
||||
src="/images/launchweek/antcopple.png"
|
||||
className="rounded-xl w-32 h-32"
|
||||
width={128}
|
||||
height={72}
|
||||
src="/images/launchweek/antcopplecall.png"
|
||||
className="rounded-xl w-32 h-32 brightness-125"
|
||||
width={120}
|
||||
height={80}
|
||||
></Image>
|
||||
</div>
|
||||
<div className="flex flex-col lg:flex-row ml-8 sm:ml-10">
|
||||
@@ -264,7 +264,7 @@ export default function launchweek() {
|
||||
justified={false}
|
||||
bordered={false}
|
||||
chevronAlign="right"
|
||||
defaultValue={[day1.d.toString()]}
|
||||
defaultValue={[day2.d.toString()]}
|
||||
>
|
||||
<div className="border-b pb-3">
|
||||
<Accordion.Item
|
||||
@@ -321,24 +321,26 @@ export default function launchweek() {
|
||||
{day2.steps.length > 0 && (
|
||||
<div className="h-[800px] lg:h-[400px] flex flex-col gap-5 lg:flex-row">
|
||||
<div
|
||||
className={`flex-1 flex flex-col justify-between basis-1/2 lg:basis-2/3 border rounded-xl h-full p-14 text-2xl bg-no-repeat bg-[url('/images/launchweek/image-processing-bg-light.png')] dark:bg-[url('/images/launchweek/image-processing-bg.png')] bg-[right_28px_top_35px] bg-contain`}
|
||||
className={`flex-1 flex flex-col items-center gap-5 lg:items-start justify-between basis-1/2 lg:basis-2/3 border rounded-xl h-full p-14 xs:text-2xl text-xl text-center bg-no-repeat bg-[url('/images/launchweek/image-processing-bg-light-alt.png')] xs:bg-[url('/images/launchweek/image-processing-bg-light.png')] dark:bg-[url('/images/launchweek/image-processing-bg-alt.png')] xs:dark:bg-[url('/images/launchweek/image-processing-bg.png')] bg-[right_30px_top_35px] lg:bg-[top_25px_right_25px] bg-contain`}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center justify-between flex-col-reverse lg:flex-row lg:justify-start gap-2 text-black dark:text-white">
|
||||
<div>{day2.steps[0].title}</div>
|
||||
<Badge className="!bg-transparent h-fit ml-4 text-sm !py-1 !px-4 text-transparent bg-clip-text bg-gradient-to-r from-[#99bbab] to-[#396f55] border-[#598973] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#598973]">
|
||||
<Badge className="!bg-transparent h-fit ml-4 text-sm !py-1 !px-4 text-transparent bg-clip-text bg-gradient-to-r from-[#99bbab] to-[#396f55] border-[#598973] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
|
||||
New
|
||||
</Badge>
|
||||
</div>
|
||||
<SectionButtons docs={day2.steps[0].docs} blog={day2.steps[0].blog} />
|
||||
</div>
|
||||
<div
|
||||
className={`flex-1 flex flex-col items-center justify-between basis-1/2 lg:basis-1/3 border rounded-xl h-full bg-no-repeat p-14 text-2xl bg-[url('/images/launchweek/cdn-caching-bg-light.png')] dark:bg-[url('/images/launchweek/cdn-caching-bg.png')] bg-[center_bottom] bg-contain`}
|
||||
className={`flex-1 flex flex-col items-center justify-between basis-1/2 lg:basis-1/3 border rounded-xl h-full bg-no-repeat p-14 text-2xl bg-[url('/images/launchweek/cdn-caching-bg-light.png')] dark:bg-[url('/images/launchweek/cdn-caching-bg.png')] bg-[top_170px_center] lg:bg-[center_bottom] bg-contain`}
|
||||
>
|
||||
<div className="flex flex-col items-center gap-2 min-w-[300px]">
|
||||
<Badge className="!bg-transparent h-fit text-sm !py-1 !px-4 text-transparent bg-clip-text bg-gradient-to-r from-[#99bbab] to-[#396f55] border-[#598973] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#598973]">
|
||||
<Badge className="!bg-transparent h-fit text-sm !py-1 !px-4 text-transparent bg-clip-text bg-gradient-to-r from-[#99bbab] to-[#396f55] border-[#598973] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
|
||||
New
|
||||
</Badge>
|
||||
<span>{day2.steps[1].title}</span>
|
||||
<span className="text-black dark:text-white">
|
||||
{day2.steps[1].title}
|
||||
</span>
|
||||
<p className="text-sm text-slate-900">{day2.steps[1].description}</p>
|
||||
</div>
|
||||
<SectionButtons docs={day2.steps[1].docs} blog={day2.steps[1].blog} />
|
||||
@@ -469,7 +471,7 @@ export default function launchweek() {
|
||||
)}
|
||||
<SectionContainer className="!pt-0 sm:!pb-20 !pb-40">
|
||||
<div className="flex flex-col mb-16">
|
||||
<Badge className="!bg-transparent h-fit text-sm !py-1 !px-4 text-transparent bg-clip-text bg-gradient-to-r from-[#99bbab] to-[#396f55] border-[#598973] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#598973] w-fit after:bg-white mb-4">
|
||||
<Badge className="mb-4 w-fit bg-gradient-to-r from-[#0E3737C2] to-[#67947F] hover:to-[#39617D94] dark:hover:to-[#A6FFD899] text-whiteA-1200 dark:text-black font-normal !py-1 !px-4 dark:from-white dark:via-white dark:to-[#1a7a4c75] bg-slate-1200">
|
||||
Happening all week
|
||||
</Badge>
|
||||
<h2 className="text-3xl text-black dark:text-white mb-2">Launch Week Hackathon</h2>
|
||||
@@ -553,7 +555,7 @@ export default function launchweek() {
|
||||
<div className="border border-slate-400 dark:border-[#2E2E2E] rounded-xl pl-5 pr-9 py-4 mb-4 bg-white dark:bg-[#121212] w-fit relative max-w-[250px]">
|
||||
<img
|
||||
src="/images/launchweek/link.svg"
|
||||
className="absolute top-[10px] right-[10px] text-brand-900"
|
||||
className="absolute top-[16px] right-[10px] text-brand-900"
|
||||
></img>
|
||||
<h3 className="text-black dark:text-white">Supabase Cache Helpers</h3>
|
||||
<p className="text-slate-1000 text-xs">Previous Best Overall Project Winner</p>
|
||||
@@ -567,7 +569,7 @@ export default function launchweek() {
|
||||
<div className="border border-slate-400 dark:border-[#2E2E2E] rounded-xl pl-5 pr-9 py-4 mb-4 bg-white dark:bg-[#121212] w-fit relative max-w-[250px]">
|
||||
<img
|
||||
src="/images/launchweek/link.svg"
|
||||
className="absolute top-[10px] right-[10px] text-brand-900"
|
||||
className="absolute top-[16px] right-[10px] text-brand-900"
|
||||
></img>
|
||||
<h3 className="text-black dark:text-white">Super UI</h3>
|
||||
<p className="text-slate-1000 text-xs">Previous Best Overall Project Runner Up</p>
|
||||
@@ -582,7 +584,7 @@ export default function launchweek() {
|
||||
<div className="border border-slate-400 dark:border-[#2E2E2E] rounded-xl pl-5 pr-9 py-4 mb-4 bg-white dark:bg-[#121212] w-fit relative max-w-[250px]">
|
||||
<img
|
||||
src="/images/launchweek/link.svg"
|
||||
className="absolute top-[10px] right-[10px] text-brand-900"
|
||||
className="absolute top-[16px] right-[10px] text-brand-900"
|
||||
></img>
|
||||
<h3 className="text-black dark:text-white">Quid Faciam Hodie?</h3>
|
||||
<p className="text-slate-1000 text-xs">Winner Best Flutter Project</p>
|
||||
@@ -592,7 +594,7 @@ export default function launchweek() {
|
||||
<div className="border border-slate-400 dark:border-[#2E2E2E] rounded-xl pl-5 pr-9 py-4 mb-4 bg-white dark:bg-[#121212] w-fit relative max-w-[250px]">
|
||||
<img
|
||||
src="/images/launchweek/link.svg"
|
||||
className="absolute top-[10px] right-[10px] text-brand-900"
|
||||
className="absolute top-[16px] right-[10px] text-brand-900"
|
||||
></img>
|
||||
<h3 className="text-black dark:text-white">That Hot Dog Game</h3>
|
||||
<p className="text-slate-1000 text-xs">Previous Winner: Most Fun/Interesting</p>
|
||||
@@ -607,7 +609,7 @@ export default function launchweek() {
|
||||
<div className="border border-slate-400 dark:border-[#2E2E2E] rounded-xl pl-5 pr-9 py-4 bg-white dark:bg-[#121212] w-fit relative max-w-[250px]">
|
||||
<img
|
||||
src="/images/launchweek/link.svg"
|
||||
className="absolute top-[10px] right-[10px] text-brand-900"
|
||||
className="absolute top-[16px] right-[10px] text-brand-900"
|
||||
></img>
|
||||
<h3 className="text-black dark:text-white">RepoWatch</h3>
|
||||
<p className="text-slate-1000 text-xs">Previous Winner Best Realtime Project</p>
|
||||
@@ -653,7 +655,7 @@ export default function launchweek() {
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<Badge className="mb-6 bg-gradient-to-r from-[#0E3737C2] to-[#67947F] hover:to-[#39617D94] dark:hover:to-[#A6FFD899] text-whiteA-1200 dark:text-black font-normal !py-1 !px-4 dark:from-white dark:via-white dark:to-[#1a7a4c75] bg-slate-1200">
|
||||
Live
|
||||
Currently Happening
|
||||
</Badge>
|
||||
<h2 className="text-3xl dark:text-white mb-2">The Supabase Content Storm</h2>
|
||||
<p className="text-slate-900 md:max-w-[80%] mb-16">
|
||||
|
||||
@@ -187,6 +187,17 @@ function StoragePage() {
|
||||
title="CDN"
|
||||
text="Serve from the edge to reduce latency."
|
||||
/>
|
||||
<Link href="/docs/guides/storage/cdn" passHref>
|
||||
<Button
|
||||
as="a"
|
||||
size="small"
|
||||
type="default"
|
||||
className="mt-4"
|
||||
icon={<IconArrowUpRight />}
|
||||
>
|
||||
Explore docs
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="col-span-6 lg:col-span-12 xl:col-span-4">
|
||||
<FeatureColumn
|
||||
@@ -194,7 +205,17 @@ function StoragePage() {
|
||||
title="Transformations"
|
||||
text="Resize and compress your media before you serve it."
|
||||
/>
|
||||
<Badge>Coming soon</Badge>
|
||||
<Link href="/docs/guides/storage/image-transformations" passHref>
|
||||
<Button
|
||||
as="a"
|
||||
size="small"
|
||||
type="default"
|
||||
className="mt-4"
|
||||
icon={<IconArrowUpRight />}
|
||||
>
|
||||
Explore docs
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>,
|
||||
]}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
import Layout from '../layouts/Layout'
|
||||
import SectionContainer from '../components/Layouts/SectionContainer'
|
||||
|
||||
export const meta = {
|
||||
id: 'supasquad',
|
||||
title: 'SupaSquad',
|
||||
description: 'Supabase Advocate program',
|
||||
}
|
||||
|
||||
<SectionContainer>
|
||||
|
||||
# SupaSquad
|
||||
|
||||
The SupaSquad is an official Supabase advocate program where community members help build and manage the Supabase community.
|
||||
|
||||
- Official recognition in the Supabase community.
|
||||
- Direct connection to the Supabase team.
|
||||
- Help steer the Supabase community.
|
||||
|
||||

|
||||

|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -89,6 +93,6 @@ Apply to join the program using [this form](https://airtable.com/shr0FtLqLfhpuEy
|
||||
the Supabase ecosystem.
|
||||
</details>
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
</SectionContainer>
|
||||
|
||||
export default Page
|
||||
export default (context) => <Layout meta={meta} children={context.children} context={context} />
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 869 KiB |
|
After Width: | Height: | Size: 353 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 362 KiB After Width: | Height: | Size: 338 KiB |
|
After Width: | Height: | Size: 550 KiB |
@@ -5,13 +5,20 @@
|
||||
<link>https://supabase.com</link>
|
||||
<description>Latest news from Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Sun, 11 Dec 2022 16:04:00 +0000</lastBuildDate>
|
||||
<lastBuildDate>Mon, 12 Dec 2022 16:04:00 +0000</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/storage-image-resizing-smart-cdn</guid>
|
||||
<title>Supabase Storage v2: Image resizing and Smart CDN</title>
|
||||
<link>https://supabase.com/blog/storage-image-resizing-smart-cdn</link>
|
||||
<description>We're introducing new features for Supabase Storage: Image resizing and a Smart CDN.</description>
|
||||
<pubDate>Mon, 12 Dec 2022 16:04:00 +0000</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/new-supabase-docs-built-with-nextjs</guid>
|
||||
<title>New Supabase Docs, built with Next.js</title>
|
||||
<link>https://supabase.com/blog/new-supabase-docs-built-with-nextjs</link>
|
||||
<description>We've redesign our Docs and migrated to Next.js</description>
|
||||
<description>We've redesignd our Docs and migrated to Next.js</description>
|
||||
<pubDate>Sun, 11 Dec 2022 16:04:00 +0000</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
@@ -66,18 +66,6 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/database</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/edge-functions</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/contact/enterprise</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -91,7 +79,13 @@
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/launch-week/ticket-image</loc>
|
||||
<loc>https://supabase.com/database</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/edge-functions</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
@@ -108,6 +102,12 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/launch-week/ticket-image</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/nextjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"@radix-ui/react-dialog": "^0.1.5",
|
||||
"@radix-ui/react-dropdown-menu": "^0.1.4",
|
||||
"@radix-ui/react-popover": "^0.1.0",
|
||||
"@radix-ui/react-portal": "^0.1.3",
|
||||
"@radix-ui/react-portal": "^1.0.1",
|
||||
"@radix-ui/react-tabs": "^0.1.0",
|
||||
"@tailwindcss/forms": "^0.4.0",
|
||||
"@tailwindcss/typography": "^0.5.0",
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import React from 'react'
|
||||
|
||||
import * as React from 'react'
|
||||
import * as RadixPopover from '@radix-ui/react-popover'
|
||||
import type * as RadixPopoverTypes from '@radix-ui/react-popover/'
|
||||
|
||||
import { IconX } from '../Icon/icons/IconX'
|
||||
|
||||
import styleHandler from '../../lib/theme/styleHandler'
|
||||
|
||||
interface RootProps {
|
||||
|
||||
@@ -382,7 +382,7 @@ export default {
|
||||
`,
|
||||
},
|
||||
'rounded-pills': {
|
||||
list: 'flex space-x-2',
|
||||
list: 'flex flex-wrap gap-2',
|
||||
base: `
|
||||
relative
|
||||
cursor-pointer
|
||||
@@ -1564,7 +1564,7 @@ export default {
|
||||
transition cursor-pointer select-none relative py-2 pl-3 pr-9
|
||||
text-scale-900
|
||||
text-sm
|
||||
|
||||
hover:bg-scale-300 dark:hover:bg-scale-500
|
||||
focus:bg-scale-300 dark:focus:bg-scale-500
|
||||
focus:text-scale-1200
|
||||
border-none
|
||||
|
||||
@@ -118,4 +118,3 @@ combine-raw.tsdoc.v2:
|
||||
###############################################################################
|
||||
format:
|
||||
npx prettier --write .
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
clispec: "001"
|
||||
clispec: '001'
|
||||
info:
|
||||
id: cli
|
||||
version: 1.23.0
|
||||
@@ -20,19 +20,19 @@ flags:
|
||||
- id: debug
|
||||
name: --debug
|
||||
description: output debug logs to stderr
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: experimental
|
||||
name: --experimental
|
||||
description: enable experimental features
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: help
|
||||
name: -h, --help
|
||||
description: help for supabase
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: workdir
|
||||
name: --workdir <string>
|
||||
description: path to a Supabase project directory
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
commands:
|
||||
- id: supabase-vanity-subdomains
|
||||
title: supabase vanity-subdomains
|
||||
@@ -53,7 +53,7 @@ commands:
|
||||
- id: project-ref
|
||||
name: --project-ref <string>
|
||||
description: Project ref of the Supabase project.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-vanity-subdomains-get
|
||||
title: supabase vanity-subdomains get
|
||||
summary: Get the current vanity subdomain
|
||||
@@ -84,7 +84,7 @@ commands:
|
||||
name: --desired-subdomain <string>
|
||||
description: |
|
||||
The desired vanity subdomain to use for your Supabase project.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-vanity-subdomains-activate
|
||||
title: supabase vanity-subdomains activate
|
||||
summary: Activate a vanity subdomain
|
||||
@@ -102,7 +102,7 @@ commands:
|
||||
name: --desired-subdomain <string>
|
||||
description: |
|
||||
The desired vanity subdomain to use for your Supabase project.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-test
|
||||
title: supabase test
|
||||
summary: Run tests on local Supabase containers
|
||||
@@ -132,7 +132,7 @@ commands:
|
||||
- id: backup
|
||||
name: --backup
|
||||
description: Backs up the current database before stopping.
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: supabase-status
|
||||
title: supabase status
|
||||
summary: Show status of local Supabase containers
|
||||
@@ -212,7 +212,7 @@ commands:
|
||||
- id: env-file
|
||||
name: --env-file <string>
|
||||
description: Read secrets from a .env file.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-secrets-list
|
||||
title: supabase secrets list
|
||||
summary: List all secrets on Supabase
|
||||
@@ -252,15 +252,15 @@ commands:
|
||||
- id: db-password
|
||||
name: --db-password <string>
|
||||
description: Database password of the project.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: interactive
|
||||
name: -i, --interactive
|
||||
description: Enables interactive mode.
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: org-id
|
||||
name: --org-id <string>
|
||||
description: Organization ID to create the project in.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: plan
|
||||
name: --plan <[ free | pro ]>
|
||||
description: Select a plan that suits your needs.
|
||||
@@ -275,7 +275,7 @@ commands:
|
||||
- id: region
|
||||
name: --region <string>
|
||||
description: Select a region close to you for the best performance.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
accepted_values:
|
||||
- id: ap-northeast-1
|
||||
name: ap-northeast-1
|
||||
@@ -347,7 +347,7 @@ commands:
|
||||
- id: project-ref
|
||||
name: --project-ref <string>
|
||||
description: Project ref of the Supabase project.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-network-restrictions-update
|
||||
title: supabase network-restrictions update
|
||||
summary: Update network restrictions
|
||||
@@ -359,7 +359,7 @@ commands:
|
||||
- id: bypass-cidr-checks
|
||||
name: --bypass-cidr-checks
|
||||
description: Bypass some of the CIDR validation checks.
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: db-allow-cidr
|
||||
name: --db-allow-cidr <strings>
|
||||
description: CIDR to allow DB connections from.
|
||||
@@ -395,7 +395,7 @@ commands:
|
||||
name: --status <[ applied | reverted ]>
|
||||
description: Version status to update.
|
||||
required: true
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
accepted_values:
|
||||
- id: applied
|
||||
name: applied
|
||||
@@ -422,7 +422,7 @@ commands:
|
||||
- id: password
|
||||
name: -p, --password <string>
|
||||
description: Password to your remote Postgres database.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-login
|
||||
title: supabase login
|
||||
summary: Authenticate using an access token
|
||||
@@ -444,12 +444,12 @@ commands:
|
||||
- id: password
|
||||
name: -p, --password <string>
|
||||
description: Password to your remote Postgres database.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: project-ref
|
||||
name: --project-ref <string>
|
||||
description: Project ref of the Supabase project.
|
||||
required: true
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-init
|
||||
title: supabase init
|
||||
summary: Initialize a local project
|
||||
@@ -489,19 +489,19 @@ commands:
|
||||
- id: db-url
|
||||
name: --db-url <string>
|
||||
description: Generate types from a database url.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: linked
|
||||
name: --linked
|
||||
description: Generate types from the linked project.
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: local
|
||||
name: --local
|
||||
description: Generate types from the local dev database.
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: project-id
|
||||
name: --project-id <string>
|
||||
description: Generate types from a project ID.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: schema
|
||||
name: --schema <stringArray>
|
||||
description: Schemas to generate types for.
|
||||
@@ -530,11 +530,11 @@ commands:
|
||||
name: --env-file <string>
|
||||
description: |
|
||||
Path to an env file to be populated to the Function environment.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: no-verify-jwt
|
||||
name: --no-verify-jwt
|
||||
description: Disable JWT verification for the Function.
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: supabase-functions-new
|
||||
title: supabase functions new
|
||||
summary: Create a new Function locally
|
||||
@@ -555,15 +555,15 @@ commands:
|
||||
- id: legacy-bundle
|
||||
name: --legacy-bundle
|
||||
description: Use legacy bundling mechanism.
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: no-verify-jwt
|
||||
name: --no-verify-jwt
|
||||
description: Disable JWT verification for the Function.
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: project-ref
|
||||
name: --project-ref <string>
|
||||
description: Project ref of the Supabase project.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-functions-delete
|
||||
title: supabase functions delete
|
||||
summary: Delete a Function from Supabase
|
||||
@@ -577,7 +577,7 @@ commands:
|
||||
- id: project-ref
|
||||
name: --project-ref <string>
|
||||
description: Project ref of the Supabase project.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-domains
|
||||
title: supabase domains
|
||||
summary: Manage custom domain names for Supabase projects
|
||||
@@ -598,11 +598,11 @@ commands:
|
||||
- id: include-raw-output
|
||||
name: --include-raw-output
|
||||
description: Include raw output (useful for debugging).
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: project-ref
|
||||
name: --project-ref <string>
|
||||
description: Project ref of the Supabase project.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-domains-reverify
|
||||
title: supabase domains reverify
|
||||
summary: Re-verify the custom hostname config for your project
|
||||
@@ -644,7 +644,7 @@ commands:
|
||||
- id: custom-hostname
|
||||
name: --custom-hostname <string>
|
||||
description: The custom hostname to use for your Supabase project.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-domains-activate
|
||||
title: supabase domains activate
|
||||
summary: Activate the custom hostname for a project
|
||||
@@ -699,7 +699,7 @@ commands:
|
||||
- id: password
|
||||
name: -p, --password <string>
|
||||
description: Password to your remote Postgres database.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-db-remote-commit
|
||||
title: supabase db remote commit
|
||||
summary: Commit remote changes as a new migration
|
||||
@@ -720,11 +720,11 @@ commands:
|
||||
name: --dry-run
|
||||
description: |
|
||||
Print the migrations that would be applied, but don't actually apply them.
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: password
|
||||
name: -p, --password <string>
|
||||
description: Password to your remote Postgres database.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: supabase-db-lint
|
||||
title: supabase db lint
|
||||
summary: Checks local database for typing error
|
||||
@@ -759,11 +759,11 @@ commands:
|
||||
- id: file
|
||||
name: -f, --file <string>
|
||||
description: Saves schema diff to a file.
|
||||
default_value: ""
|
||||
default_value: ''
|
||||
- id: linked
|
||||
name: --linked
|
||||
description: Diffs local schema against linked project.
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: schema
|
||||
name: -s, --schema <strings>
|
||||
description: List of schema to include.
|
||||
@@ -771,7 +771,7 @@ commands:
|
||||
- id: use-migra
|
||||
name: --use-migra
|
||||
description: Use migra to generate schema diff.
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: supabase-completion
|
||||
title: supabase completion
|
||||
summary: Generate the autocompletion script for the specified shell
|
||||
@@ -821,7 +821,7 @@ commands:
|
||||
- id: no-descriptions
|
||||
name: --no-descriptions
|
||||
description: disable completion descriptions
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: supabase-completion-powershell
|
||||
title: supabase completion powershell
|
||||
summary: Generate the autocompletion script for powershell
|
||||
@@ -842,7 +842,7 @@ commands:
|
||||
- id: no-descriptions
|
||||
name: --no-descriptions
|
||||
description: disable completion descriptions
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: supabase-completion-fish
|
||||
title: supabase completion fish
|
||||
summary: Generate the autocompletion script for fish
|
||||
@@ -866,7 +866,7 @@ commands:
|
||||
- id: no-descriptions
|
||||
name: --no-descriptions
|
||||
description: disable completion descriptions
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
- id: supabase-completion-bash
|
||||
title: supabase completion bash
|
||||
summary: Generate the autocompletion script for bash
|
||||
@@ -899,4 +899,4 @@ commands:
|
||||
- id: no-descriptions
|
||||
name: --no-descriptions
|
||||
description: disable completion descriptions
|
||||
default_value: "false"
|
||||
default_value: 'false'
|
||||
@@ -267,12 +267,6 @@
|
||||
"slug": "supabase-db-remote-commit",
|
||||
"type": "cli-command"
|
||||
},
|
||||
{
|
||||
"id": "supabase-db-remote-changes",
|
||||
"title": "supabase db remote changes",
|
||||
"slug": "supabase-db-remote-changes",
|
||||
"type": "cli-command"
|
||||
},
|
||||
{
|
||||
"id": "supabase-db-push",
|
||||
"title": "supabase db push",
|
||||
|
||||
@@ -932,7 +932,7 @@
|
||||
},
|
||||
{
|
||||
"id": "from-get-public-url",
|
||||
"title": "Retrieve an asset's URL",
|
||||
"title": "Retrieve public URL",
|
||||
"slug": "storage-from-getpublicurl",
|
||||
"product": "storage",
|
||||
"type": "function",
|
||||
|
||||
@@ -1112,7 +1112,8 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 5,
|
||||
"name": "SchemaName"
|
||||
"name": "SchemaName",
|
||||
"dereferenced": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1291,7 +1292,8 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 5,
|
||||
"name": "SchemaName"
|
||||
"name": "SchemaName",
|
||||
"dereferenced": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2157,7 +2157,138 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 357,
|
||||
"name": "Bucket"
|
||||
"name": "Bucket",
|
||||
"dereferenced": {
|
||||
"id": 357,
|
||||
"name": "Bucket",
|
||||
"kind": 256,
|
||||
"kindString": "Interface",
|
||||
"flags": {},
|
||||
"children": [
|
||||
{
|
||||
"id": 361,
|
||||
"name": "created_at",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 5,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 358,
|
||||
"name": "id",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 2,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 359,
|
||||
"name": "name",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 3,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 360,
|
||||
"name": "owner",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 4,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 363,
|
||||
"name": "public",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 7,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 362,
|
||||
"name": "updated_at",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 6,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [361, 358, 359, 360, 363, 362]
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 1,
|
||||
"character": 17
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2544,7 +2675,70 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 374,
|
||||
"name": "SortBy"
|
||||
"name": "SortBy",
|
||||
"dereferenced": {
|
||||
"id": 374,
|
||||
"name": "SortBy",
|
||||
"kind": 256,
|
||||
"kindString": "Interface",
|
||||
"flags": {},
|
||||
"children": [
|
||||
{
|
||||
"id": 375,
|
||||
"name": "column",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 23,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 376,
|
||||
"name": "order",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 24,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [375, 376]
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 22,
|
||||
"character": 17
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -4724,7 +4918,112 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 390,
|
||||
"name": "TransformOptions"
|
||||
"name": "TransformOptions",
|
||||
"dereferenced": {
|
||||
"id": 390,
|
||||
"name": "TransformOptions",
|
||||
"kind": 256,
|
||||
"kindString": "Interface",
|
||||
"flags": {},
|
||||
"children": [
|
||||
{
|
||||
"id": 392,
|
||||
"name": "height",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"comment": {
|
||||
"shortText": "The height of the image in pixels."
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 84,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 393,
|
||||
"name": "resize",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"comment": {
|
||||
"shortText": "The resize mode can be cover, contain or fill. Defaults to cover.\nCover resizes the image to maintain it's aspect ratio while filling the entire width and height.\nContain resizes the image to maintain it's aspect ratio while fitting the entire image within the width and height.\nFill resizes the image to fill the entire width and height. If the object's aspect ratio does not match the width and height, the image will be stretched to fit."
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 91,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "union",
|
||||
"types": [
|
||||
{
|
||||
"type": "literal",
|
||||
"value": "cover"
|
||||
},
|
||||
{
|
||||
"type": "literal",
|
||||
"value": "contain"
|
||||
},
|
||||
{
|
||||
"type": "literal",
|
||||
"value": "fill"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 391,
|
||||
"name": "width",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"comment": {
|
||||
"shortText": "The width of the image in pixels."
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 80,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "number"
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [392, 393, 391]
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 76,
|
||||
"character": 17
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -5306,7 +5605,112 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 390,
|
||||
"name": "TransformOptions"
|
||||
"name": "TransformOptions",
|
||||
"dereferenced": {
|
||||
"id": 390,
|
||||
"name": "TransformOptions",
|
||||
"kind": 256,
|
||||
"kindString": "Interface",
|
||||
"flags": {},
|
||||
"children": [
|
||||
{
|
||||
"id": 392,
|
||||
"name": "height",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"comment": {
|
||||
"shortText": "The height of the image in pixels."
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 84,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 393,
|
||||
"name": "resize",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"comment": {
|
||||
"shortText": "The resize mode can be cover, contain or fill. Defaults to cover.\nCover resizes the image to maintain it's aspect ratio while filling the entire width and height.\nContain resizes the image to maintain it's aspect ratio while fitting the entire image within the width and height.\nFill resizes the image to fill the entire width and height. If the object's aspect ratio does not match the width and height, the image will be stretched to fit."
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 91,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "union",
|
||||
"types": [
|
||||
{
|
||||
"type": "literal",
|
||||
"value": "cover"
|
||||
},
|
||||
{
|
||||
"type": "literal",
|
||||
"value": "contain"
|
||||
},
|
||||
{
|
||||
"type": "literal",
|
||||
"value": "fill"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 391,
|
||||
"name": "width",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"comment": {
|
||||
"shortText": "The width of the image in pixels."
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 80,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "number"
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [392, 393, 391]
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 76,
|
||||
"character": 17
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -5558,7 +5962,112 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 390,
|
||||
"name": "TransformOptions"
|
||||
"name": "TransformOptions",
|
||||
"dereferenced": {
|
||||
"id": 390,
|
||||
"name": "TransformOptions",
|
||||
"kind": 256,
|
||||
"kindString": "Interface",
|
||||
"flags": {},
|
||||
"children": [
|
||||
{
|
||||
"id": 392,
|
||||
"name": "height",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"comment": {
|
||||
"shortText": "The height of the image in pixels."
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 84,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 393,
|
||||
"name": "resize",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"comment": {
|
||||
"shortText": "The resize mode can be cover, contain or fill. Defaults to cover.\nCover resizes the image to maintain it's aspect ratio while filling the entire width and height.\nContain resizes the image to maintain it's aspect ratio while fitting the entire image within the width and height.\nFill resizes the image to fill the entire width and height. If the object's aspect ratio does not match the width and height, the image will be stretched to fit."
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 91,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "union",
|
||||
"types": [
|
||||
{
|
||||
"type": "literal",
|
||||
"value": "cover"
|
||||
},
|
||||
{
|
||||
"type": "literal",
|
||||
"value": "contain"
|
||||
},
|
||||
{
|
||||
"type": "literal",
|
||||
"value": "fill"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 391,
|
||||
"name": "width",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"comment": {
|
||||
"shortText": "The width of the image in pixels."
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 80,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "number"
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [392, 393, 391]
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 76,
|
||||
"character": 17
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -5795,7 +6304,70 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 374,
|
||||
"name": "SortBy"
|
||||
"name": "SortBy",
|
||||
"dereferenced": {
|
||||
"id": 374,
|
||||
"name": "SortBy",
|
||||
"kind": 256,
|
||||
"kindString": "Interface",
|
||||
"flags": {},
|
||||
"children": [
|
||||
{
|
||||
"id": 375,
|
||||
"name": "column",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 23,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 376,
|
||||
"name": "order",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isOptional": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 24,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [375, 376]
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 22,
|
||||
"character": 17
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -460,7 +460,8 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 78,
|
||||
"name": "SchemaName"
|
||||
"name": "SchemaName",
|
||||
"dereferenced": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -526,7 +527,31 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 60,
|
||||
"name": "Fetch"
|
||||
"name": "Fetch",
|
||||
"dereferenced": {
|
||||
"id": 60,
|
||||
"name": "Fetch",
|
||||
"kind": 4194304,
|
||||
"kindString": "Type alias",
|
||||
"flags": {},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 8,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "query",
|
||||
"queryType": {
|
||||
"type": "reference",
|
||||
"id": 18,
|
||||
"qualifiedName": "fetch",
|
||||
"package": "typescript",
|
||||
"name": "fetch"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2750,7 +2775,8 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 78,
|
||||
"name": "SchemaName"
|
||||
"name": "SchemaName",
|
||||
"dereferenced": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -2816,7 +2842,31 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 60,
|
||||
"name": "Fetch"
|
||||
"name": "Fetch",
|
||||
"dereferenced": {
|
||||
"id": 60,
|
||||
"name": "Fetch",
|
||||
"kind": 4194304,
|
||||
"kindString": "Type alias",
|
||||
"flags": {},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 8,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "query",
|
||||
"queryType": {
|
||||
"type": "reference",
|
||||
"id": 18,
|
||||
"qualifiedName": "fetch",
|
||||
"package": "typescript",
|
||||
"name": "fetch"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -3829,7 +3879,8 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 78,
|
||||
"name": "SchemaName"
|
||||
"name": "SchemaName",
|
||||
"dereferenced": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -3895,7 +3946,31 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 60,
|
||||
"name": "Fetch"
|
||||
"name": "Fetch",
|
||||
"dereferenced": {
|
||||
"id": 60,
|
||||
"name": "Fetch",
|
||||
"kind": 4194304,
|
||||
"kindString": "Type alias",
|
||||
"flags": {},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 8,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "query",
|
||||
"queryType": {
|
||||
"type": "reference",
|
||||
"id": 18,
|
||||
"qualifiedName": "fetch",
|
||||
"package": "typescript",
|
||||
"name": "fetch"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -4253,7 +4328,8 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 78,
|
||||
"name": "SchemaName"
|
||||
"name": "SchemaName",
|
||||
"dereferenced": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -4319,7 +4395,31 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 60,
|
||||
"name": "Fetch"
|
||||
"name": "Fetch",
|
||||
"dereferenced": {
|
||||
"id": 60,
|
||||
"name": "Fetch",
|
||||
"kind": 4194304,
|
||||
"kindString": "Type alias",
|
||||
"flags": {},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 8,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "query",
|
||||
"queryType": {
|
||||
"type": "reference",
|
||||
"id": 18,
|
||||
"qualifiedName": "fetch",
|
||||
"package": "typescript",
|
||||
"name": "fetch"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -5608,7 +5708,8 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 78,
|
||||
"name": "SchemaName"
|
||||
"name": "SchemaName",
|
||||
"dereferenced": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -5674,7 +5775,31 @@
|
||||
"type": {
|
||||
"type": "reference",
|
||||
"id": 60,
|
||||
"name": "Fetch"
|
||||
"name": "Fetch",
|
||||
"dereferenced": {
|
||||
"id": 60,
|
||||
"name": "Fetch",
|
||||
"kind": 4194304,
|
||||
"kindString": "Type alias",
|
||||
"flags": {},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/lib/types.ts",
|
||||
"line": 8,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "query",
|
||||
"queryType": {
|
||||
"type": "reference",
|
||||
"id": 18,
|
||||
"qualifiedName": "fetch",
|
||||
"package": "typescript",
|
||||
"name": "fetch"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -69,6 +69,17 @@ function dereferenceReducer(child: any, kv: KV) {
|
||||
if (!!child.parameters) {
|
||||
child.parameters.forEach((x: any) => dereferenceReducer(x, kv))
|
||||
}
|
||||
if (
|
||||
!!child.type &&
|
||||
!!child.type.declaration &&
|
||||
!!child.type.declaration.children &&
|
||||
child.type.type === 'reflection'
|
||||
) {
|
||||
child.type.declaration.children.forEach((x: any) =>
|
||||
dereferenceReducer(x, kv)
|
||||
)
|
||||
}
|
||||
|
||||
const final = { ...child }
|
||||
if (
|
||||
// For now I can only dereference parameters
|
||||
@@ -81,6 +92,14 @@ function dereferenceReducer(child: any, kv: KV) {
|
||||
const dereferenced = kv[final.type.id]
|
||||
final.type.dereferenced = dereferenced || {}
|
||||
return final
|
||||
} else if (
|
||||
final.kindString === 'Property' &&
|
||||
final.type?.type === 'reference' &&
|
||||
final.type?.id
|
||||
) {
|
||||
const dereferenced = kv[final.type.id]
|
||||
final.type.dereferenced = dereferenced || {}
|
||||
return final
|
||||
} else if (
|
||||
final.kindString === 'Type alias' &&
|
||||
final.type?.type === 'union'
|
||||
|
||||
@@ -504,7 +504,7 @@ functions:
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```dart
|
||||
final data = await supabase
|
||||
await supabase
|
||||
.from('cities')
|
||||
.insert({'name': 'The Shire', 'country_id': 554});
|
||||
```
|
||||
@@ -512,11 +512,21 @@ functions:
|
||||
name: Bulk create
|
||||
code: |
|
||||
```dart
|
||||
final res = await supabase.from('cities').insert([
|
||||
await supabase.from('cities').insert([
|
||||
{'name': 'The Shire', 'country_id': 554},
|
||||
{'name': 'Rohan', 'country_id': 555},
|
||||
]);
|
||||
```
|
||||
- id: fetch-inserted-data
|
||||
name: Fetch inserted record
|
||||
code: |
|
||||
```dart
|
||||
final List<Map<String, dynamic>> data =
|
||||
await supabase.from('cities').insert([
|
||||
{'name': 'The Shire', 'country_id': 554},
|
||||
{'name': 'Rohan', 'country_id': 555},
|
||||
]).select();
|
||||
```
|
||||
|
||||
- id: update
|
||||
description: |
|
||||
@@ -526,17 +536,17 @@ functions:
|
||||
- `update()` should always be combined with [Filters](/docs/reference/dart/using-filters) to target the item(s) you wish to update.
|
||||
examples:
|
||||
- id: updating-your-data
|
||||
name: Updating your data
|
||||
name: Update your data
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```dart
|
||||
final data = await supabase
|
||||
await supabase
|
||||
.from('cities')
|
||||
.update({ 'name': 'Middle Earth' })
|
||||
.match({ 'name': 'Auckland' });
|
||||
```
|
||||
- id: updating-json-data
|
||||
name: Updating JSON data
|
||||
name: Update JSON data
|
||||
description: |
|
||||
Postgres offers a
|
||||
[number of operators](https://www.postgresql.org/docs/current/functions-json.html)
|
||||
@@ -544,7 +554,7 @@ functions:
|
||||
but we are [working on ideas](https://github.com/PostgREST/postgrest/issues/465) for updating individual keys.
|
||||
code: |
|
||||
```dart
|
||||
final data = await supabase
|
||||
await supabase
|
||||
.from('users')
|
||||
.update({
|
||||
'address': {
|
||||
@@ -554,6 +564,18 @@ functions:
|
||||
})
|
||||
.eq('address->postcode', 90210);
|
||||
```
|
||||
- id: fetch-updated-rows
|
||||
name: Fetch updated rows
|
||||
code: |
|
||||
```dart
|
||||
final List<Map<String, dynamic>> data = await supabase
|
||||
.from('users')
|
||||
.update({
|
||||
'address': {'street': 'Melrose Place', 'postcode': 90210}
|
||||
})
|
||||
.eq('address->postcode', 90210)
|
||||
.select();
|
||||
```
|
||||
|
||||
- id: upsert
|
||||
description: |
|
||||
@@ -568,7 +590,7 @@ functions:
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```dart
|
||||
final data = await supabase
|
||||
await supabase
|
||||
.from('messages')
|
||||
.upsert({ 'id': 3, 'message': 'foo', 'username': 'supabot' });
|
||||
```
|
||||
@@ -581,7 +603,7 @@ functions:
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```dart
|
||||
final data = await supabase
|
||||
await supabase
|
||||
.from('users')
|
||||
.upsert({ 'username': 'supabot' }, { 'onConflict': 'username' });
|
||||
```
|
||||
@@ -612,11 +634,21 @@ functions:
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```dart
|
||||
final data = await supabase
|
||||
await supabase
|
||||
.from('cities')
|
||||
.delete()
|
||||
.match({ 'id': 666 });
|
||||
```
|
||||
- id: fetch-delete-records
|
||||
name: Fetch deleted records
|
||||
code: |
|
||||
```dart
|
||||
final List<Map<String,dynamic>> data = await supabase
|
||||
.from('cities')
|
||||
.delete()
|
||||
.match({ 'id': 666 })
|
||||
.select();
|
||||
```
|
||||
|
||||
- id: rpc
|
||||
title: 'Stored Procedures: rpc()'
|
||||
@@ -1082,10 +1114,26 @@ functions:
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```dart
|
||||
final String signedUrl = await supabase
|
||||
.storage
|
||||
.from('avatars')
|
||||
.createSignedUrl('avatar1.png', 60);
|
||||
final String signedUrl = await supabase
|
||||
.storage
|
||||
.from('avatars')
|
||||
.createSignedUrl('avatar1.png', 60);
|
||||
```
|
||||
- id: create-signed-url-with-transform
|
||||
name: With transform
|
||||
code: |
|
||||
```dart
|
||||
final String signedUrl = await supabase
|
||||
.storage
|
||||
.from('avatars')
|
||||
.createSignedUrl(
|
||||
'avatar1.png',
|
||||
60,
|
||||
transform: TransformOptions(
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
- id: from-get-public-url
|
||||
@@ -1103,10 +1151,26 @@ functions:
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```dart
|
||||
final String publicUrl = supabase
|
||||
.storage
|
||||
.from('public-bucket')
|
||||
.getPublicUrl('avatar1.png');
|
||||
final String publicUrl = supabase
|
||||
.storage
|
||||
.from('public-bucket')
|
||||
.getPublicUrl('avatar1.png');
|
||||
```
|
||||
- id: returns-the-url-for-an-asset-in-a-public-bucket-with-transform
|
||||
name: With transform
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```dart
|
||||
final String publicUrl = await supabase
|
||||
.storage
|
||||
.from('public-bucket')
|
||||
.getPublicUrl(
|
||||
'avatar1.png',
|
||||
transform: TransformOptions(
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
- id: from-download
|
||||
@@ -1128,6 +1192,22 @@ functions:
|
||||
.from('avatars')
|
||||
.download('avatar1.png');
|
||||
```
|
||||
- id: download-file-with-transform
|
||||
name: With transform
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```dart
|
||||
final Uint8List file = await supabase
|
||||
.storage
|
||||
.from('avatars')
|
||||
.download(
|
||||
'avatar1.png',
|
||||
transform: TransformOptions(
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
- id: from-remove
|
||||
description: |
|
||||
@@ -1272,178 +1352,151 @@ functions:
|
||||
Filters can be used on `select()`, `update()`, and `delete()` queries.
|
||||
|
||||
If a Database function returns a table response, you can also apply filters.
|
||||
examples:
|
||||
- id: applying-filters
|
||||
name: Applying Filters
|
||||
description: |
|
||||
Filters must be applied after any of `select()`, `update()`, `upsert()`,
|
||||
`delete()`, and `rpc()` and before
|
||||
[modifiers](/docs/reference/dart/using-modifiers).
|
||||
code: |
|
||||
```dart
|
||||
final data = await supabase
|
||||
.from('cities')
|
||||
.select('name, country_id')
|
||||
.eq('name', 'The Shire'); // Correct
|
||||
|
||||
### Applying Filters
|
||||
final data = await supabase
|
||||
.from('cities')
|
||||
.eq('name', 'The Shire') // Incorrect
|
||||
.select('name, country_id');
|
||||
```
|
||||
- id: chaining-filters
|
||||
name: Chaining Filters
|
||||
description: |
|
||||
Filters must be applied after any of `select()`, `update()`, `upsert()`,
|
||||
`delete()`, and `rpc()` and before
|
||||
[modifiers](/docs/reference/dart/using-modifiers).
|
||||
code: |
|
||||
```dart
|
||||
final data = await supabase
|
||||
.from('cities')
|
||||
.select('name, country_id')
|
||||
.eq('name', 'The Shire'); // Correct
|
||||
|
||||
Filters must be applied after any of `select()`, `update()`, `upsert()`,
|
||||
`delete()`, and `rpc()` and before
|
||||
[modifiers](/docs/reference/dart/using-modifiers).
|
||||
final data = await supabase
|
||||
.from('cities')
|
||||
.eq('name', 'The Shire') // Incorrect
|
||||
.select('name, country_id');
|
||||
```
|
||||
- id: conditional-chaining
|
||||
name: Conditional Chaining
|
||||
description: |
|
||||
Filters can be built up one step at a time and then executed. For example:
|
||||
code: |
|
||||
```dart
|
||||
final data = await supabase
|
||||
.from('users')
|
||||
.select()
|
||||
.eq('address->postcode', 90210);
|
||||
```
|
||||
- id: filter-by-value-within-json-column
|
||||
name: Filter by values within a JSON column
|
||||
description: |
|
||||
Filters can be built up one step at a time and then executed. For example:
|
||||
data:
|
||||
sql: |
|
||||
```sql
|
||||
create table
|
||||
users (
|
||||
id int8 primary key,
|
||||
name text,
|
||||
address jsonb
|
||||
);
|
||||
|
||||
```dart
|
||||
final data = await supabase
|
||||
.from('cities')
|
||||
.select('name, country_id')
|
||||
.eq('name', 'The Shire'); // Correct
|
||||
|
||||
final data = await supabase
|
||||
.from('cities')
|
||||
.eq('name', 'The Shire') // Incorrect
|
||||
.select('name, country_id');
|
||||
```
|
||||
|
||||
### Chaining
|
||||
|
||||
Filters can be chained together to produce advanced queries. For example,
|
||||
to query cities with population between 1,000 and 10,000:
|
||||
|
||||
```dart
|
||||
final data = await supabase
|
||||
.from('cities')
|
||||
.select('name, country_id')
|
||||
.gte('population', 1000)
|
||||
.lt('population', 10000);
|
||||
```
|
||||
|
||||
### Conditional Chaining
|
||||
|
||||
Filters can be built up one step at a time and then executed. For example:
|
||||
|
||||
```dart
|
||||
final filterByName = null;
|
||||
final filterPopLow = 1000;
|
||||
final filterPopHigh = 10000;
|
||||
|
||||
var query = supabase
|
||||
.from('cities')
|
||||
.select('name, country_id');
|
||||
|
||||
if (filterByName != null) { query = query.eq('name', filterByName); }
|
||||
if (filterPopLow != null) { query = query.gte('population', filterPopLow); }
|
||||
if (filterPopHigh != null) { query = query.lt('population', filterPopHigh); }
|
||||
|
||||
final data = await query;
|
||||
```
|
||||
|
||||
### Filter by values within a JSON column
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="schema" label="Schema">
|
||||
|
||||
```sql
|
||||
create table
|
||||
users (
|
||||
id int8 primary key,
|
||||
name text,
|
||||
address jsonb
|
||||
);
|
||||
|
||||
insert into
|
||||
users (id, name, address)
|
||||
values
|
||||
(1, 'Michael', '{ "postcode": 90210 }'),
|
||||
(2, 'Jane', null);
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem default value="dart" label="Flutter">
|
||||
|
||||
```dart
|
||||
final data = await supabase
|
||||
.from('users')
|
||||
.select()
|
||||
.eq('address->postcode', 90210);
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="result" label="Result">
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Michael",
|
||||
"address": {
|
||||
"postcode": 90210
|
||||
}
|
||||
}
|
||||
],
|
||||
"status": 200,
|
||||
"statusText": "OK"
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Filter Foreign Tables
|
||||
|
||||
You can filter on foreign tables in your `select()` query using dot
|
||||
notation:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="schema" label="Schema">
|
||||
|
||||
```sql
|
||||
create table
|
||||
countries (id int8 primary key, name text);
|
||||
create table
|
||||
cities (
|
||||
id int8 primary key,
|
||||
country_id int8 not null references countries,
|
||||
name text
|
||||
);
|
||||
|
||||
insert into
|
||||
countries (id, name)
|
||||
values
|
||||
(1, 'Germany'),
|
||||
(2, 'Indonesia');
|
||||
insert into
|
||||
cities (id, country_id, name)
|
||||
values
|
||||
(1, 2, 'Bali'),
|
||||
(2, 1, 'Munich');
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem default value="dart" label="Flutter">
|
||||
|
||||
```dart
|
||||
final data = await supabase
|
||||
.from('countries')
|
||||
.select('''
|
||||
name,
|
||||
cities!inner (
|
||||
name
|
||||
)
|
||||
''')
|
||||
.eq('cities.name', 'Bali');
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="result" label="Result">
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"name": "Indonesia",
|
||||
"cities": [
|
||||
{
|
||||
"name": "Bali"
|
||||
insert into
|
||||
users (id, name, address)
|
||||
values
|
||||
(1, 'Michael', '{ "postcode": 90210 }'),
|
||||
(2, 'Jane', null);
|
||||
```
|
||||
response: |
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Michael",
|
||||
"address": {
|
||||
"postcode": 90210
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"status": 200,
|
||||
"statusText": "OK"
|
||||
}
|
||||
```
|
||||
code: |
|
||||
```dart
|
||||
final data = await supabase
|
||||
.from('users')
|
||||
.select()
|
||||
.eq('address->postcode', 90210);
|
||||
```
|
||||
- id: filter-foreign-tables
|
||||
name: Filter Foreign Tables
|
||||
code: |
|
||||
```dart
|
||||
final data = await supabase
|
||||
.from('countries')
|
||||
.select('''
|
||||
name,
|
||||
cities!inner (
|
||||
name
|
||||
)
|
||||
''')
|
||||
.eq('cities.name', 'Bali');
|
||||
```
|
||||
data:
|
||||
sql: |
|
||||
```sql
|
||||
create table
|
||||
countries (id int8 primary key, name text);
|
||||
create table
|
||||
cities (
|
||||
id int8 primary key,
|
||||
country_id int8 not null references countries,
|
||||
name text
|
||||
);
|
||||
|
||||
insert into
|
||||
countries (id, name)
|
||||
values
|
||||
(1, 'Germany'),
|
||||
(2, 'Indonesia');
|
||||
insert into
|
||||
cities (id, country_id, name)
|
||||
values
|
||||
(1, 2, 'Bali'),
|
||||
(2, 1, 'Munich');
|
||||
```
|
||||
response: |
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"name": "Indonesia",
|
||||
"cities": [
|
||||
{
|
||||
"name": "Bali"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"status": 200,
|
||||
"statusText": "OK"
|
||||
}
|
||||
],
|
||||
"status": 200,
|
||||
"statusText": "OK"
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
```
|
||||
- id: or
|
||||
title: or()
|
||||
description: |
|
||||
|
||||
@@ -7404,6 +7404,33 @@ functions:
|
||||
.from('avatars')
|
||||
.createSignedUrl('folder/avatar1.png', 60)
|
||||
```
|
||||
- id: create-signed-url-with-transformations
|
||||
name: Create a signed URL for an asset with transformations
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```js
|
||||
const { data } = supabase
|
||||
.storage
|
||||
.from('avatars')
|
||||
.createSignedUrl('folder/avatar1.png', 60, {
|
||||
transform: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
}
|
||||
})
|
||||
```
|
||||
- id: create-signed-url-with-download
|
||||
name: Create a signed URL which triggers the download of the asset
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```js
|
||||
const { data } = supabase
|
||||
.storage
|
||||
.from('avatars')
|
||||
.createSignedUrl('folder/avatar1.png', 60, {
|
||||
download: true,
|
||||
})
|
||||
```
|
||||
|
||||
- id: from-create-signed-urls
|
||||
title: from.createSignedUrls()
|
||||
@@ -7445,6 +7472,33 @@ functions:
|
||||
.from('public-bucket')
|
||||
.getPublicUrl('folder/avatar1.png')
|
||||
```
|
||||
- id: transform-asset-in-public-bucket
|
||||
name: Returns the URL for an asset in a public bucket with transformations
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```js
|
||||
const { data } = supabase
|
||||
.storage
|
||||
.from('public-bucket')
|
||||
.getPublicUrl('folder/avatar1.png', {
|
||||
transform: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
}
|
||||
})
|
||||
```
|
||||
- id: download-asset-in-public-bucket
|
||||
name: Returns the URL which triggers the download of an asset in a public bucket
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```js
|
||||
const { data } = supabase
|
||||
.storage
|
||||
.from('public-bucket')
|
||||
.getPublicUrl('folder/avatar1.png', {
|
||||
download: true,
|
||||
})
|
||||
```
|
||||
|
||||
- id: from-download
|
||||
title: from.download()
|
||||
@@ -7465,6 +7519,21 @@ functions:
|
||||
.from('avatars')
|
||||
.download('folder/avatar1.png')
|
||||
```
|
||||
- id: download-file-with-transformations
|
||||
name: Download file with transformations
|
||||
isSpotlight: true
|
||||
code: |
|
||||
```js
|
||||
const { data, error } = await supabase
|
||||
.storage
|
||||
.from('avatars')
|
||||
.download('folder/avatar1.png', {
|
||||
transform: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
- id: from-remove
|
||||
title: from.remove()
|
||||
|
||||
@@ -56,19 +56,21 @@ const EnableExtensionModal: FC<Props> = ({ visible, extension, onCancel }) => {
|
||||
setSubmitting(true)
|
||||
|
||||
const schema =
|
||||
defaultSchema !== undefined
|
||||
defaultSchema !== undefined && defaultSchema !== null
|
||||
? defaultSchema
|
||||
: values.schema === 'custom'
|
||||
? values.name
|
||||
: values.schema
|
||||
|
||||
const { error: createSchemaError } = await meta.query(`create schema if not exists ${schema}`)
|
||||
if (createSchemaError) {
|
||||
return ui.setNotification({
|
||||
error: createSchemaError,
|
||||
category: 'error',
|
||||
message: `Failed to create schema: ${createSchemaError.message}`,
|
||||
})
|
||||
if (!schema.startsWith('pg_')) {
|
||||
const { error: createSchemaError } = await meta.query(`create schema if not exists ${schema}`)
|
||||
if (createSchemaError) {
|
||||
return ui.setNotification({
|
||||
error: createSchemaError,
|
||||
category: 'error',
|
||||
message: `Failed to create schema: ${createSchemaError.message}`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const { error: createExtensionError } = await meta.extensions.create({
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Form, IconDatabase, Input, Listbox, SidePanel, Modal, IconPlus } from '
|
||||
import { useStore } from 'hooks'
|
||||
import { Table, TableOption } from './Wrappers.types'
|
||||
import { makeValidateRequired } from './Wrappers.utils'
|
||||
import MultiSelect from 'components/ui/MultiSelect'
|
||||
import ActionBar from 'components/interfaces/TableGridEditor/SidePanelEditor/ActionBar'
|
||||
|
||||
export type WrapperTableEditorProps = {
|
||||
@@ -201,25 +200,44 @@ const TableForm = ({
|
||||
{requiredOptions.map((option) => (
|
||||
<Option key={option.name} option={option} />
|
||||
))}
|
||||
<MultiSelect
|
||||
label="Columns"
|
||||
options={table.availableColumns.map((column) => {
|
||||
return {
|
||||
id: column.name,
|
||||
value: column.name,
|
||||
name: column.name,
|
||||
description: column.type,
|
||||
disabled: false,
|
||||
}
|
||||
})}
|
||||
error={errors.columns}
|
||||
value={values.columns}
|
||||
placeholder="Select at least one column"
|
||||
searchPlaceholder="Search for a column"
|
||||
onChange={(columns) => {
|
||||
resetForm({ values: { ...values, columns } })
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="form-group">
|
||||
<label className="!w-full">Select the columns to be added to your table</label>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{table.availableColumns.map((column) => {
|
||||
const isSelected = values.columns.includes(column.name)
|
||||
return (
|
||||
<div
|
||||
key={column.name}
|
||||
className={[
|
||||
'px-2 py-1 bg-scale-500 rounded cursor-pointer transition',
|
||||
`${isSelected ? 'bg-brand-800' : 'hover:bg-scale-700'}`,
|
||||
].join(' ')}
|
||||
onClick={() => {
|
||||
if (isSelected) {
|
||||
resetForm({
|
||||
values: {
|
||||
...values,
|
||||
columns: values.columns.filter((x: string) => x !== column.name),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
resetForm({
|
||||
values: { ...values, columns: values.columns.concat([column.name]) },
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<p className="text-sm">{column.name}</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{errors.columns && (
|
||||
<span className="text-red-900 text-sm mt-2">{errors.columns}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{optionalOptions.map((option) => (
|
||||
<Option key={option.name} option={option} />
|
||||
))}
|
||||
|
||||
@@ -108,18 +108,22 @@ const AccountLayout: FC<Props> = ({ children, title, breadcrumbs }) => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'logout-link',
|
||||
links: [
|
||||
{
|
||||
key: `logout`,
|
||||
icon: '/icons/feather/power.svg',
|
||||
label: 'Logout',
|
||||
href: undefined,
|
||||
onClick: onClickLogout,
|
||||
},
|
||||
],
|
||||
},
|
||||
...(IS_PLATFORM
|
||||
? [
|
||||
{
|
||||
key: 'logout-link',
|
||||
links: [
|
||||
{
|
||||
key: `logout`,
|
||||
icon: '/icons/feather/power.svg',
|
||||
label: 'Logout',
|
||||
href: undefined,
|
||||
onClick: onClickLogout,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
|
||||
return (
|
||||
@@ -132,7 +136,7 @@ const AccountLayout: FC<Props> = ({ children, title, breadcrumbs }) => {
|
||||
<div className="flex h-full">
|
||||
<main
|
||||
style={{ height: maxHeight, maxHeight }}
|
||||
className="flex w-full flex-1 flex-col overflow-y-auto"
|
||||
className="flex flex-col flex-1 w-full overflow-y-auto"
|
||||
>
|
||||
<WithSidebar title={title} breadcrumbs={breadcrumbs} sections={sectionsWithHeaders}>
|
||||
{children}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useStore } from 'hooks'
|
||||
import { usePushNext } from 'hooks/misc/useAutoAuthRedirect'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import { auth, getReturnToPath, STORAGE_KEY } from 'lib/gotrue'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import Head from 'next/head'
|
||||
@@ -77,19 +78,21 @@ const SignInLayout = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window._getReturnToPath = ${getReturnToPath.toString()};if (localStorage.getItem('${STORAGE_KEY}') && !(new URLSearchParams(location.search).has('next'))) {location.replace(window._getReturnToPath())}`,
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
{IS_PLATFORM && (
|
||||
<Head>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window._getReturnToPath = ${getReturnToPath.toString()};if (localStorage.getItem('${STORAGE_KEY}') && !(new URLSearchParams(location.search).has('next'))) {location.replace(window._getReturnToPath())}`,
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
)}
|
||||
|
||||
<div className="flex-1 flex flex-col bg-scale-100">
|
||||
<div className="absolute top-0 mx-auto w-full px-8 pt-6 sm:px-6 lg:px-8">
|
||||
<div className="flex flex-col flex-1 bg-scale-100">
|
||||
<div className="absolute top-0 w-full px-8 pt-6 mx-auto sm:px-6 lg:px-8">
|
||||
<nav className="relative flex items-center justify-between sm:h-10">
|
||||
<div className="flex flex-shrink-0 flex-grow items-center lg:flex-grow-0">
|
||||
<div className="flex w-full items-center justify-between md:w-auto">
|
||||
<div className="flex items-center flex-grow flex-shrink-0 lg:flex-grow-0">
|
||||
<div className="flex items-center justify-between w-full md:w-auto">
|
||||
<Link href={logoLinkToMarketingSite ? 'https://supabase.com' : '/projects'}>
|
||||
<a>
|
||||
<Image
|
||||
@@ -103,7 +106,7 @@ const SignInLayout = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hidden items-center space-x-3 md:ml-10 md:flex md:pr-4">
|
||||
<div className="items-center hidden space-x-3 md:ml-10 md:flex md:pr-4">
|
||||
<Link href="https://supabase.com/docs">
|
||||
<a target="_blank">
|
||||
<Button type="default" icon={<IconFileText />}>
|
||||
@@ -115,12 +118,12 @@ const SignInLayout = ({
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 flex">
|
||||
<main className="flex-1 flex-shrink-0 flex flex-col items-center bg-scale-200 px-5 pt-16 pb-8 border-r border-scale-500 shadow-lg">
|
||||
<div className="flex flex-1">
|
||||
<main className="flex flex-col items-center flex-1 flex-shrink-0 px-5 pt-16 pb-8 border-r shadow-lg bg-scale-200 border-scale-500">
|
||||
<div className="flex-1 flex flex-col justify-center w-[384px]">
|
||||
<div className="mb-10">
|
||||
<h1 className="text-2xl lg:text-3xl mt-8 mb-2">{heading}</h1>
|
||||
<h2 className="text-scale-1100 text-sm">{subheading}</h2>
|
||||
<h1 className="mt-8 mb-2 text-2xl lg:text-3xl">{heading}</h1>
|
||||
<h2 className="text-sm text-scale-1100">{subheading}</h2>
|
||||
</div>
|
||||
|
||||
{children}
|
||||
@@ -143,14 +146,14 @@ const SignInLayout = ({
|
||||
)}
|
||||
</main>
|
||||
|
||||
<aside className="hidden flex-1 flex-shrink basis-1/4 xl:flex flex-col justify-center items-center">
|
||||
<aside className="flex-col items-center justify-center flex-1 flex-shrink hidden basis-1/4 xl:flex">
|
||||
{quote !== null && (
|
||||
<div className="relative flex flex-col gap-6">
|
||||
<div className="absolute -top-12 -left-11 select-none">
|
||||
<div className="absolute select-none -top-12 -left-11">
|
||||
<span className="text-[160px] leading-none text-scale-600">{'“'}</span>
|
||||
</div>
|
||||
|
||||
<blockquote className="text-3xl z-10 max-w-lg">{quote.text}</blockquote>
|
||||
<blockquote className="z-10 max-w-lg text-3xl">{quote.text}</blockquote>
|
||||
|
||||
<a
|
||||
href={quote.url}
|
||||
@@ -161,11 +164,11 @@ const SignInLayout = ({
|
||||
<img
|
||||
src={`https://supabase.com${quote.img_url}`}
|
||||
alt={quote.handle}
|
||||
className="rounded-full w-12 h-12"
|
||||
className="w-12 h-12 rounded-full"
|
||||
/>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<cite className="font-medium not-italic text-scale-1100 whitespace-nowrap">
|
||||
<cite className="not-italic font-medium text-scale-1100 whitespace-nowrap">
|
||||
@{quote.handle}
|
||||
</cite>
|
||||
</div>
|
||||
|
||||
@@ -51,12 +51,11 @@ const ConfirmationModal: FC<Props> = ({
|
||||
size={size}
|
||||
onCancel={onSelectCancel}
|
||||
customFooter={
|
||||
<div className="flex w-full items-center space-x-3">
|
||||
<Button block type="secondary" disabled={loading} onClick={onSelectCancel}>
|
||||
<div className="flex justify-end w-full items-center space-x-3">
|
||||
<Button type="default" disabled={loading} onClick={onSelectCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
block
|
||||
type={danger ? 'danger' : 'primary'}
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { useRef, useEffect, useState, FormEvent, KeyboardEvent, ReactNode } from 'react'
|
||||
import { orderBy, filter, without } from 'lodash'
|
||||
import { Popover, IconCheck, IconAlertCircle, IconSearch, IconPlus, IconChevronDown } from 'ui'
|
||||
import {
|
||||
Popover,
|
||||
IconCheck,
|
||||
IconAlertCircle,
|
||||
IconSearch,
|
||||
IconPlus,
|
||||
IconChevronDown,
|
||||
Input,
|
||||
} from 'ui'
|
||||
|
||||
import { BadgeDisabled, BadgeSelected } from './Badges'
|
||||
|
||||
|
||||
@@ -84,11 +84,13 @@ export function withAuth<T>(
|
||||
<Head>
|
||||
{/* This script will quickly (before the main JS loads) redirect the user
|
||||
to the login page if they are guaranteed (no token at all) to not be logged in. */}
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window._getReturnToPath = ${getReturnToPath.toString()};if (!localStorage.getItem('${STORAGE_KEY}') && !location.hash) {const searchParams = new URLSearchParams(location.search);searchParams.set('returnTo', location.pathname);location.replace('/sign-in' + '?' + searchParams.toString())}`,
|
||||
}}
|
||||
/>
|
||||
{IS_PLATFORM && (
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window._getReturnToPath = ${getReturnToPath.toString()};if (!localStorage.getItem('${STORAGE_KEY}') && !location.hash) {const searchParams = new URLSearchParams(location.search);searchParams.set('returnTo', location.pathname);location.replace('/sign-in' + '?' + searchParams.toString())}`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Head>
|
||||
<WrappedComponent {...props} />
|
||||
</>
|
||||
@@ -105,7 +107,7 @@ export function withAuth<T>(
|
||||
}
|
||||
|
||||
function defaultRedirectTo(ref: string | string[] | undefined) {
|
||||
return IS_PLATFORM ? '/sign-in' : ref !== undefined ? `/project/${ref}` : '/sign-in'
|
||||
return IS_PLATFORM ? '/sign-in' : ref !== undefined ? `/project/${ref}` : '/projects'
|
||||
}
|
||||
|
||||
function checkRedirectTo(
|
||||
|
||||
@@ -107,7 +107,6 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
<Head>
|
||||
<title>Supabase</title>
|
||||
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
|
||||
<link rel="stylesheet" type="text/css" href="/css/fonts.css" />
|
||||
</Head>
|
||||
|
||||
<PageTelemetry>
|
||||
|
||||
@@ -18,6 +18,7 @@ class MyDocument extends Document {
|
||||
data-name="vs/editor/editor.main"
|
||||
href="https://cdn.jsdelivr.net/npm/monaco-editor@0.28.1/min/vs/editor/editor.main.css"
|
||||
/>
|
||||
<link rel="stylesheet" type="text/css" href="/css/fonts.css" />
|
||||
</Head>
|
||||
<body className="dark">
|
||||
<Main />
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { NextPage } from 'next'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { isUndefined, isNaN } from 'lodash'
|
||||
import { Checkbox, Modal } from 'ui'
|
||||
import { Alert, Button, Checkbox, IconExternalLink, Modal } from 'ui'
|
||||
import { PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
|
||||
|
||||
import Base64 from 'lib/base64'
|
||||
@@ -124,7 +125,7 @@ const TableEditorPage: NextPage = () => {
|
||||
|
||||
const onConfirmDeleteColumn = async () => {
|
||||
try {
|
||||
const response: any = await meta.columns.del(selectedColumnToDelete!.id)
|
||||
const response: any = await meta.columns.del(selectedColumnToDelete!.id, isDeleteWithCascade)
|
||||
if (response.error) throw response.error
|
||||
|
||||
removeDeletedColumnFromFiltersAndSorts(selectedColumnToDelete!.name)
|
||||
@@ -200,22 +201,54 @@ const TableEditorPage: NextPage = () => {
|
||||
/>
|
||||
<ConfirmationModal
|
||||
danger
|
||||
size="small"
|
||||
visible={isDeleting && !isUndefined(selectedColumnToDelete)}
|
||||
header={`Confirm deletion of column "${selectedColumnToDelete?.name}"`}
|
||||
children={
|
||||
<Modal.Content>
|
||||
<p className="py-4 text-sm text-scale-1100">
|
||||
Are you sure you want to delete the selected column? This action cannot be undone.
|
||||
</p>
|
||||
<div className="py-4 space-y-4">
|
||||
<p className="text-sm text-scale-1100">
|
||||
Are you sure you want to delete the selected column? This action cannot be undone.
|
||||
</p>
|
||||
<Checkbox
|
||||
label="Drop column with cascade?"
|
||||
description="Deletes the column and its dependent objects"
|
||||
checked={isDeleteWithCascade}
|
||||
onChange={() => setIsDeleteWithCascade(!isDeleteWithCascade)}
|
||||
/>
|
||||
{isDeleteWithCascade && (
|
||||
<Alert
|
||||
withIcon
|
||||
variant="warning"
|
||||
title="Warning: Dropping with cascade may result in unintended consequences"
|
||||
>
|
||||
<p className="mb-4">
|
||||
All dependent objects will be removed, as will any objects that depend on them,
|
||||
recursively.
|
||||
</p>
|
||||
<Link href="https://www.postgresql.org/docs/current/ddl-depend.html">
|
||||
<a target="_blank">
|
||||
<Button size="tiny" type="default" icon={<IconExternalLink />}>
|
||||
About dependency tracking
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</Modal.Content>
|
||||
}
|
||||
buttonLabel="Delete"
|
||||
buttonLoadingLabel="Deleting"
|
||||
onSelectCancel={() => setIsDeleting(false)}
|
||||
onSelectCancel={() => {
|
||||
setIsDeleting(false)
|
||||
setSelectedColumnToDelete(undefined)
|
||||
}}
|
||||
onSelectConfirm={onConfirmDeleteColumn}
|
||||
/>
|
||||
<ConfirmationModal
|
||||
danger
|
||||
size="small"
|
||||
visible={isDeleting && !isUndefined(selectedTableToDelete)}
|
||||
header={
|
||||
<span className="break-words">{`Confirm deletion of table "${selectedTableToDelete?.name}"`}</span>
|
||||
@@ -232,12 +265,34 @@ const TableEditorPage: NextPage = () => {
|
||||
checked={isDeleteWithCascade}
|
||||
onChange={() => setIsDeleteWithCascade(!isDeleteWithCascade)}
|
||||
/>
|
||||
{isDeleteWithCascade && (
|
||||
<Alert
|
||||
withIcon
|
||||
variant="warning"
|
||||
title="Warning: Dropping with cascade may result in unintended consequences"
|
||||
>
|
||||
<p className="mb-4">
|
||||
All dependent objects will be removed, as will any objects that depend on them,
|
||||
recursively.
|
||||
</p>
|
||||
<Link href="https://www.postgresql.org/docs/current/ddl-depend.html">
|
||||
<a target="_blank">
|
||||
<Button size="tiny" type="default" icon={<IconExternalLink />}>
|
||||
About dependency tracking
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</Modal.Content>
|
||||
}
|
||||
buttonLabel="Delete"
|
||||
buttonLoadingLabel="Deleting"
|
||||
onSelectCancel={() => setIsDeleting(false)}
|
||||
onSelectCancel={() => {
|
||||
setIsDeleting(false)
|
||||
setSelectedTableToDelete(undefined)
|
||||
}}
|
||||
onSelectConfirm={onConfirmDeleteTable}
|
||||
/>
|
||||
</TableEditorLayout>
|
||||
|
||||
@@ -6,10 +6,21 @@ import SignInForm from 'components/interfaces/SignIn/SignInForm'
|
||||
import SignInWithGitHub from 'components/interfaces/SignIn/SignInWithGitHub'
|
||||
import SignInWithSSO from 'components/interfaces/SignIn/SignInWithSSO'
|
||||
import { SignInLayout } from 'components/layouts'
|
||||
import { useEffect } from 'react'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
const SignInPage: NextPageWithLayout = () => {
|
||||
const showSsoLogin = useFlag('mfaSso')
|
||||
|
||||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
// if the dashboard is running locally, redirect straight to the projects page
|
||||
if (!IS_PLATFORM) {
|
||||
router.replace('/projects')
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-5">
|
||||
@@ -21,18 +32,18 @@ const SignInPage: NextPageWithLayout = () => {
|
||||
<div className="w-full border-t border-scale-700" />
|
||||
</div>
|
||||
<div className="relative flex justify-center text-sm">
|
||||
<span className="bg-scale-200 px-2 text-sm text-scale-1200">or</span>
|
||||
<span className="px-2 text-sm bg-scale-200 text-scale-1200">or</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SignInForm />
|
||||
</div>
|
||||
|
||||
<div className="my-8 self-center text-sm">
|
||||
<div className="self-center my-8 text-sm">
|
||||
<div>
|
||||
<span className="text-scale-1000">Don't have an account?</span>{' '}
|
||||
<Link href="/sign-up">
|
||||
<a className="underline text-scale-1200 hover:text-scale-1100 transition">
|
||||
<a className="underline transition text-scale-1200 hover:text-scale-1100">
|
||||
Sign Up Now
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
@@ -420,7 +420,7 @@ export default class MetaStore implements IMetaStore {
|
||||
this.rootStore.ui.setNotification({
|
||||
id: toastId,
|
||||
category: 'error',
|
||||
message: `An error occured while creating the column "${payload.name}"`,
|
||||
message: `An error occurred while creating the column "${payload.name}"`,
|
||||
})
|
||||
const query = `alter table "${selectedTable.name}" drop column if exists "${payload.name}";`
|
||||
await this.rootStore.meta.query(query)
|
||||
|
||||