Merge branch 'master' of github.com:supabase/supabase

This commit is contained in:
Terry Sutton committed 2022-12-23 09:43:26 -03:30
commit ff31550bf3
198 files changed
+6146 -19161

No files matched your search

+1
View File
@@ -2,3 +2,4 @@
/apps/www/ @supabase/frontend
/apps/docs/ @supabase/docs
/spec/ @supabase/docs
/docker/ @supabase/cli
+1 -1
View File
@@ -142,7 +142,7 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
<td>-</td>
<td><a href="https://github.com/supabase-community/gotrue-java" target="_blank" rel="noopener noreferrer">gotrue-java</a></td>
<td>-</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/storage-java" target="_blanke" rel="noopener noreferrer">storage-java</a></td>
<td>-</td>
</tr>
<tr>
+3 -3
View File
@@ -5158,9 +5158,9 @@
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
},
"decompress-response": {
"version": "3.3.0",
+2 -2
View File
@@ -1,4 +1,4 @@
import { useState, useCallback, useRef, createContext, useContext, useEffect } from 'react'
import { useState, useCallback, useRef, createContext, useContext, useEffect, memo } from 'react'
import { createPortal } from 'react-dom'
import Link from 'next/link'
import Head from 'next/head'
@@ -84,7 +84,7 @@ export function SearchProvider({ children }: any) {
hitComponent={Hit}
transformItems={(items) => {
return items.map((item, index) => {
console.log('item', item)
// console.log('item', item)
// We transform the absolute URL into a relative URL to
// leverage Next's preloading.
const a = document.createElement('a')
@@ -1,45 +0,0 @@
import { useTheme } from 'common/Providers'
import { IconSun, IconMoon } from '@supabase/ui'
function DarkModeToggle() {
const { isDarkMode, toggleTheme } = useTheme()
const toggleDarkMode = () => {
localStorage.setItem('supabaseDarkMode', (!isDarkMode).toString())
toggleTheme()
const key = localStorage.getItem('supabaseDarkMode')
document.documentElement.className = key === 'true' ? 'dark' : ''
}
return (
<div className="flex items-center">
<IconSun className="text-scale-900" strokeWidth={2} />
<button
type="button"
aria-pressed="false"
className={`
relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent
transition-colors duration-200 ease-in-out focus:outline-none ${
isDarkMode ? 'bg-scale-500' : 'bg-scale-900'
} mx-5
`}
onClick={() => toggleDarkMode()}
>
<span className="sr-only">Toggle Themes</span>
<span
aria-hidden="true"
className={`
${
isDarkMode ? 'translate-x-5' : 'translate-x-0'
} dark:bg-scale-300 inline-block h-5 w-5
transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out
`}
/>
</button>
<IconMoon className="text-scale-900" strokeWidth={2} />
</div>
)
}
export default DarkModeToggle
-133
View File
@@ -1,133 +0,0 @@
import { Badge } from 'ui'
import Link from 'next/link'
import Image from 'next/image'
import FooterLinks from 'data/footer.json'
import DarkModeToggle from './DarkModeToggle'
import { useTheme } from 'common/Providers'
import * as supabaseLogoWordmarkDark from 'common/assets/images/supabase-logo-wordmark--dark.png'
import * as supabaseLogoWordmarkLight from 'common/assets/images/supabase-logo-wordmark--light.png'
const Footer = () => {
const { isDarkMode } = useTheme()
return (
<footer
className="border-scale-500 dark:border-scale-600 border-t"
aria-labelledby="footerHeading"
>
<h2 id="footerHeading" className="sr-only">
Footer
</h2>
<div className="sm:py-18 container relative mx-auto px-6 py-16 md:py-24 lg:px-16 lg:py-24 xl:px-20">
<div className="xl:grid xl:grid-cols-3 xl:gap-8">
<div className="space-y-8 xl:col-span-1">
<Link href="#" as="/">
<a className="w-40">
<Image
src={isDarkMode ? supabaseLogoWordmarkDark : supabaseLogoWordmarkLight}
width={160}
height={30}
alt="Supabase"
/>
</a>
</Link>
<div className="flex space-x-5">
<a
href="https://twitter.com/supabase"
className="text-scale-900 hover:text-scale-1200 transition"
>
<span className="sr-only">Twitter</span>
<svg className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
</svg>
</a>
<a
href="https://github.com/supabase"
className="text-scale-900 hover:text-scale-1200 transition"
>
<span className="sr-only">GitHub</span>
<svg className="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path
fillRule="evenodd"
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
clipRule="evenodd"
/>
</svg>
</a>
<a
href="https://discord.supabase.com/"
className="text-scale-900 hover:text-scale-1200 transition"
>
<span className="sr-only">Discord</span>
<svg className="h-6 w-6" fill="currentColor" viewBox="0 0 71 55" aria-hidden="true">
<path
fillRule="evenodd"
d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z"
clipRule="evenodd"
/>
</svg>
</a>
<a
href="https://youtube.com/c/supabase"
className="text-scale-900 hover:text-scale-1200 transition"
>
<span className="sr-only">Youtube</span>
<svg
className="h-6 w-6"
fill="currentColor"
viewBox="0 0 576 512"
aria-hidden="true"
>
<path d="M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z" />
</svg>
</a>
</div>
</div>
<div className="mt-12 grid grid-cols-1 gap-8 xl:col-span-2 xl:mt-0">
<div className="grid grid-cols-2 gap-8 md:grid-cols-4">
{FooterLinks.map((segment: any) => {
return (
<div key={`footer_${segment.title}`}>
<h6 className="text-scale-1200 overwrite text-base">{segment.title}</h6>
<ul className="mt-4 space-y-2">
{segment.links.map((link: any, idx: number) => (
<li key={`${segment.title}_link_${idx}`}>
<a
href={link.url}
className={`text-sm transition-colors ${
link.url
? 'text-scale-1100 hover:text-scale-1200 '
: 'text-scale-900 hover:text-scale-900'
} `}
>
{link.text}
{!link.url && (
<div className="ml-2 inline text-xs xl:ml-0 xl:block 2xl:ml-2 2xl:inline">
<Badge color="scale" size="small">
Coming soon
</Badge>
</div>
)}
</a>
</li>
))}
</ul>
</div>
)
})}
</div>
</div>
</div>
<div className="border-scale-500 dark:border-scale-600 mt-32 flex justify-between border-t pt-8">
<small className="small">&copy; Supabase Inc</small>
<DarkModeToggle />
</div>
</div>
</footer>
)
}
export default Footer
+8 -6
View File
@@ -1,11 +1,13 @@
const FooterHelpCallout = () => {
return (
<div className="min-w-full px-8 py-6 prose bg-scale-300 dark:bg-whiteA-200 rounded">
<h5 className="text-sm m-0 text-scale-1100">Need some help?</h5>
<p className="m-0 text-sm text-scale-900">
Not to worry, our specialist engineers are here to help. Submit a support ticket through the{' '}
<a href="https://app.supabase.com/support/new">Dashboard</a>.
</p>
<div className="mt-32">
<div className="min-w-full px-8 py-6 prose bg-scale-300 dark:bg-whiteA-200 rounded">
<h5 className="text-sm m-0 text-scale-1100">Need some help?</h5>
<p className="m-0 text-sm text-scale-900">
Not to worry, our specialist engineers are here to help. Submit a support ticket through
the <a href="https://app.supabase.com/support/new">Dashboard</a>.
</p>
</div>
</div>
)
}
@@ -0,0 +1,56 @@
import Image from 'next/image'
import Link from 'next/link'
import { Button, IconGitHub, IconTwitter } from '~/../../packages/ui'
import footerData from '~/data/footer.json'
import { useTheme } from 'common/Providers'
const Footer = () => {
const { isDarkMode } = useTheme()
return (
<div>
<hr className="border-scale-400 mt-8"></hr>
<div className="flex gap-4 items-center mt-6 justify-between">
<div className="flex flex-col lg:flex-row gap-3 ">
<span className="text-xs text-scale-900">Supabase 2022</span>
<span className="text-xs text-scale-900">—</span>
{footerData.map((item) => (
<Link href={item.url} key={item.url}>
<a className="text-xs text-scale-800 hover:underline">{item.title}</a>
</Link>
))}
</div>
<div className="flex items-center gap-2">
<Button
type="text"
as="a"
// @ts-ignore
href="https://twitter.com/supabase"
target="_blank"
rel="noreferrer noopener"
>
<IconTwitter size={16} />
</Button>
<Button
type="text"
as="a"
// @ts-ignore
href="https://discord.supabase.com/"
target="_blank"
rel="noreferrer noopener"
>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="16">
<path
fill="none"
fillRule="nonzero"
stroke={`${isDarkMode ? '#fff' : '#333'}`}
d="M16.238 2.16a14.628 14.628 0 0 0-3.664-1.159.055.055 0 0 0-.059.028c-.158.288-.333.662-.456.957a13.432 13.432 0 0 0-4.115 0 9.76 9.76 0 0 0-.464-.957.057.057 0 0 0-.058-.028 14.587 14.587 0 0 0-3.664 1.16.053.053 0 0 0-.024.02C1.4 5.74.76 9.21 1.074 12.637c.002.017.011.033.024.043a14.813 14.813 0 0 0 4.494 2.318.058.058 0 0 0 .064-.02c.346-.483.654-.992.92-1.527a.059.059 0 0 0-.032-.08c-.49-.19-.956-.421-1.404-.684a.06.06 0 0 1-.006-.097c.094-.072.189-.148.279-.223a.055.055 0 0 1 .058-.008c2.946 1.372 6.135 1.372 9.046 0a.055.055 0 0 1 .06.007 7.2 7.2 0 0 0 .279.224.06.06 0 0 1-.005.097 9.143 9.143 0 0 1-1.405.683.06.06 0 0 0-.03.081c.27.534.578 1.043.918 1.526.014.02.04.029.063.021A14.763 14.763 0 0 0 18.9 12.68a.06.06 0 0 0 .023-.042c.376-3.962-.628-7.404-2.66-10.455a.046.046 0 0 0-.024-.021Zm-9.223 8.39c-.887 0-1.618-.831-1.618-1.852 0-1.02.717-1.851 1.618-1.851.908 0 1.632.838 1.618 1.851 0 1.02-.717 1.851-1.618 1.851Zm5.981 0c-.887 0-1.618-.831-1.618-1.852 0-1.02.717-1.851 1.618-1.851.908 0 1.632.838 1.618 1.851 0 1.02-.71 1.851-1.618 1.851Z"
/>
</svg>
</Button>
</div>
</div>
</div>
)
}
export default Footer
@@ -1,8 +1,8 @@
import { useTheme } from 'common/Providers'
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'
import { Fragment } from 'react'
const home = [
[
@@ -64,6 +64,12 @@ const home = [
href: '/guides/resources',
level: 'resources',
},
{
label: 'Self-Hosting',
icon: '/img/icons/menu/platform',
href: '/guides/self-hosting',
level: 'self_hosting',
},
{
label: 'Integrations',
icon: '/img/icons/menu/integrations',
@@ -106,8 +112,7 @@ const home = [
],
]
const NavigationMenuHome = () => {
const level = useMenuLevelId()
const NavigationMenuHome = ({ active }) => {
const router = useRouter()
const { isDarkMode } = useTheme()
@@ -115,15 +120,13 @@ const NavigationMenuHome = () => {
<div
className={[
'transition-all duration-150 ease-out',
level === 'home' || !level
? 'opacity-100 ml-0 delay-150'
: 'opacity-0 -ml-8 invisible absolute',
active ? '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 (
<>
<Fragment key={`section-container-${sectionIndex}-border`}>
{sectionIndex !== 0 && (
<div
className="h-px w-full bg-blackA-300 dark:bg-whiteA-300"
@@ -170,7 +173,7 @@ const NavigationMenuHome = () => {
})}
</div>
</div>
</>
</Fragment>
)
})}
</ul>
@@ -14,7 +14,7 @@ export const REFERENCES: References = {
name: 'supabase-js',
library: 'supabase-js',
versions: ['v2', 'v1'],
icon: '/docs/img/libraries/javascript-icon.svg',
icon: '/img/libraries/javascript-icon',
},
dart: {
name: 'Flutter',
@@ -570,45 +570,37 @@ export const resources = {
},
],
},
{
name: 'Self-Hosting',
items: [
{ name: 'Overview', url: '/guides/resources/self-hosting', items: [] },
{ name: 'Docker', url: '/guides/resources/self-hosting/docker', items: [] },
],
},
{
name: 'Self-Hosting Reference',
items: [
{
name: 'Auth Server',
url: '/reference/self-hosting-auth/introduction',
icon: '/img/icons/menu/reference-auth',
items: [],
},
{
name: 'Storage Server',
url: '/reference/self-hosting-storage/introduction',
icon: '/img/icons/menu/reference-storage',
items: [],
},
{
name: 'Realtime Server',
url: '/reference/self-hosting-realtime/introduction',
icon: '/img/icons/menu/reference-realtime',
items: [],
},
],
},
],
}
export const selfHosting = {
label: 'Self-Hosting',
export const self_hosting = {
title: 'Self-Hosting',
icon: 'resources',
url: '/guides/self-hosting',
items: [
{ name: 'Overview', url: '/guides/hosting/overview', items: [] },
{ name: 'Docker', url: '/guides/hosting/docker', items: [] },
{ name: 'Overview', url: '/guides/self-hosting', items: [] },
{ name: 'Self-Hosting with Docker', url: '/guides/self-hosting/docker', items: [] },
{
name: 'Auth Server',
items: [
{ name: 'Reference', url: '/reference/self-hosting-auth/introduction', items: [] },
{ name: 'Configuration', url: '/guides/self-hosting/auth/config', items: [] },
],
},
{
name: 'Storage Server',
items: [
{ name: 'Reference', url: '/reference/self-hosting-storage/introduction', items: [] },
{ name: 'Configuration', url: '/guides/self-hosting/storage/config', items: [] },
],
},
{
name: 'Realtime Server',
items: [
{ name: 'Reference', url: '/reference/self-hosting-realtime/introduction', items: [] },
{ name: 'Configuration', url: '/guides/self-hosting/realtime/config', items: [] },
],
},
],
}
@@ -696,22 +688,22 @@ export const reference = {
url: '/reference/javascript/start',
level: 'reference_javascript',
items: [],
icon: 'docs/img/icons/javascript.svg',
icon: '/img/icons/menu/reference-javascript',
},
{
name: 'supabase-dart',
url: '/reference/dart/start',
level: 'reference_dart',
items: [],
icon: 'docs/img/icons/javascript.svg',
},
{
name: 'supbase-python',
url: '/reference/python/start',
level: 'reference_python',
items: [],
icon: 'docs/img/icons/javascript.svg',
icon: '/img/icons/menu/reference-dart',
},
// {
// name: 'supabase-python',
// url: '/reference/python/start',
// level: 'reference_python',
// items: [],
// icon: '/img/icons/menu/reference-javascript',
// },
],
},
{
@@ -721,13 +713,13 @@ export const reference = {
name: 'Supabase CLI',
url: '/reference/cli/start',
items: [],
icon: 'docs/img/icons/cli-icon.svg',
icon: '/img/icons/menu/reference-cli',
},
{
name: 'Management API',
url: '/reference/javascript',
items: [],
icon: 'docs/img/icons/javascript-icon.svg',
icon: '/img/icons/menu/reference-api',
},
],
},
@@ -1,6 +1,6 @@
import { useRouter } from 'next/router'
import { memo, useEffect } from 'react'
import { menuState } from '~/hooks/useMenuState'
import { menuState, useMenuLevelId } from '~/hooks/useMenuState'
import NavigationMenuGuideList from './NavigationMenuGuideList'
import NavigationMenuRefList from './NavigationMenuRefList'
// @ts-expect-error
@@ -22,7 +22,7 @@ import { flattenSections } from '~/lib/helpers'
import NavigationMenuHome from './HomeMenu'
// Filter libCommonSections for just the relevant sections in the current library
function generateAllowedClientLibKeys(sections, spec) {
export function generateAllowedClientLibKeys(sections, spec) {
// Filter parent sections first
const specIds = spec.functions.map((func) => {
@@ -62,7 +62,7 @@ export type RefKeyOptions =
| 'self-hosting-storage'
| 'self-hosting-realtime'
const SideNav = () => {
const NavigationMenu = () => {
const router = useRouter()
let version = ''
@@ -104,6 +104,9 @@ const SideNav = () => {
case url.includes(`/docs/guides/resources`) && url:
menuState.setMenuLevelId('resources')
break
case url.includes(`/docs/guides/self-hosting`) && url:
menuState.setMenuLevelId('self_hosting')
break
case url.includes(`/docs/guides/integrations`) && url:
menuState.setMenuLevelId('integrations')
break
@@ -153,59 +156,91 @@ const SideNav = () => {
}
}, [router.events])
const level = useMenuLevelId()
const isHomeActive = 'home' === level
const isGettingStartedActive = 'gettingstarted' === level
const isDatabaseActive = 'database' === level
const isAuthActive = 'auth' === level
const isFunctionsActive = 'functions' === level
const isRealtimeActive = 'realtime' === level
const isStorageActive = 'storage' === level
const isPlatformActive = 'platform' === level
const isResourcesActive = 'resources' === level
const isSelfHosting = 'self_hosting' === level
const isIntegrationsActive = 'integrations' === level
const isReferenceActive = 'reference' === level
const isReference_Javascript_V1 = 'reference_javascript_v1' === level
const isReference_Javascript_V2 = 'reference_javascript_v2' === level
const isReference_Dart_V0 = 'reference_dart_v0' === level
const isReference_Dart_V1 = 'reference_dart_v1' === level
const isReference_Cli = 'reference_cli' === level
const isReference_Api = 'reference_api' === level
const isReference_Self_Hosting_Auth = 'reference_self_hosting_auth' === level
const isReference_Self_Hosting_Storage = 'reference_self_hosting_storage' === level
const isReference_Self_Hosting_Realtime = 'reference_self_hosting_realtime' === level
return (
<div className="flex relative">
<div className={['flex relative', 'justify-center lg:justify-start'].join(' ')}>
{/* // main menu */}
<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'} />
<NavigationMenuHome active={isHomeActive} />
<NavigationMenuGuideList id={'gettingstarted'} active={isGettingStartedActive} />
<NavigationMenuGuideList id={'database'} active={isDatabaseActive} />
<NavigationMenuGuideList id={'auth'} active={isAuthActive} />
<NavigationMenuGuideList id={'functions'} active={isFunctionsActive} />
<NavigationMenuGuideList id={'realtime'} active={isRealtimeActive} />
<NavigationMenuGuideList id={'storage'} active={isStorageActive} />
<NavigationMenuGuideList id={'platform'} active={isPlatformActive} />
<NavigationMenuGuideList id={'resources'} active={isResourcesActive} />
<NavigationMenuGuideList id={'self_hosting'} active={isSelfHosting} />
<NavigationMenuGuideList id={'integrations'} active={isIntegrationsActive} />
<NavigationMenuGuideList id={'reference'} active={isReferenceActive} />
{/* // Client Libs */}
<NavigationMenuRefList
key={'reference-js-menu'}
key={'reference-js-menu-v1'}
id={'reference_javascript_v1'}
active={isReference_Javascript_V1}
commonSections={libCommonSections}
lib="javascript"
allowedClientKeys={generateAllowedClientLibKeys(libCommonSections, spec_js_v1)}
spec={spec_js_v1}
/>
<NavigationMenuRefList
key={'reference-js-menu'}
id={'reference_javascript_v2'}
active={isReference_Javascript_V2}
commonSections={libCommonSections}
lib="javascript"
allowedClientKeys={generateAllowedClientLibKeys(libCommonSections, spec_js_v2)}
spec={spec_js_v2}
/>
<NavigationMenuRefList
key={'reference-dart-menu'}
id={'reference_dart_v0'}
active={isReference_Dart_V0}
commonSections={libCommonSections}
lib="dart"
allowedClientKeys={generateAllowedClientLibKeys(libCommonSections, spec_dart_v0)}
spec={spec_dart_v0}
/>
<NavigationMenuRefList
key={'reference-dart-menu'}
key={'reference-dart-menu-v1'}
id={'reference_dart_v1'}
active={isReference_Dart_V1}
commonSections={libCommonSections}
lib="dart"
allowedClientKeys={generateAllowedClientLibKeys(libCommonSections, spec_dart_v1)}
spec={spec_dart_v1}
/>
{/* // Tools */}
<NavigationMenuRefList
key={'reference-cli-menu'}
id={'reference_cli'}
active={isReference_Cli}
commonSections={cliCommonSections}
lib="cli"
/>
<NavigationMenuRefList
key={'reference-api-menu'}
id={'reference_api'}
active={isReference_Api}
commonSections={apiCommonSections}
lib="api"
/>
@@ -213,18 +248,21 @@ const SideNav = () => {
<NavigationMenuRefList
key={'reference-self-hosting-auth-menu'}
id={'reference_self_hosting_auth'}
active={isReference_Self_Hosting_Auth}
commonSections={authServerCommonSections}
lib="self-hosting-auth"
/>
<NavigationMenuRefList
key={'reference-self-hosting-storage-menu'}
id={'reference_self_hosting_storage'}
active={isReference_Self_Hosting_Storage}
commonSections={storageServerCommonSections}
lib="self-hosting-storage"
/>
<NavigationMenuRefList
key={'reference-self-hosting-realtime-menu'}
id={'reference_self_hosting_realtime'}
active={isReference_Self_Hosting_Realtime}
commonSections={realtimeServerCommonSections}
lib="self-hosting-auth"
/>
@@ -232,4 +270,4 @@ const SideNav = () => {
)
}
export default memo(SideNav)
export default memo(NavigationMenu)
@@ -0,0 +1,6 @@
// check if the link is allowed to be displayed
export function isFuncNotInLibraryOrVersion(id, type, allowedKeys) {
if (id && allowedKeys && !allowedKeys.includes(id) && type !== 'markdown') {
return true
}
}
@@ -140,28 +140,6 @@ const NavigationMenuCliList = ({ currentLevel, setLevel, id }) => {
<Divider />
</ul>
{menu.extras && (
<>
<Divider />{' '}
<span className="font-mono text-xs uppercase text-scale-1200 font-medium tracking-wider mb-2">
Resources
</span>
</>
)}
{menu.extras?.map((x) => {
return (
<div key={x.name}>
<li>
<Link href={`${x.href}`} passHref>
<a className="cursor-pointer transition text-scale-1100 text-sm hover:text-brand-900 flex gap-3 my-1">
{x.icon && <img className="w-4" src={`${router.basePath}${x.icon}`} />}
{x.name}
</a>
</Link>
</li>
</div>
)
})}
</div>
</div>
)
@@ -1,32 +1,20 @@
import * as Accordion from '@radix-ui/react-accordion'
import { useTheme } from 'common/Providers'
import Image from 'next/image'
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'
import NavigationMenuGuideListItems from './NavigationMenuGuideListItems'
interface Props {
id: string
setMenuLevelId?: any
active: boolean
}
const NavigationMenuGuideList: React.FC<Props> = ({ id, setMenuLevelId }) => {
const NavigationMenuGuideList: React.FC<Props> = ({ id, active }) => {
const router = useRouter()
const { isDarkMode } = useTheme()
const menu = NavItems[id]
//console.log(id, 'url is:', menu.url)
// get url
const url = router.asPath
// 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
@@ -36,194 +24,15 @@ const NavigationMenuGuideList: React.FC<Props> = ({ id, setMenuLevelId }) => {
className={[
'transition-all ml-8 duration-150 ease-out',
// enabled
level === id && 'opacity-100 ml-0 delay-150',
level === 'home' && 'ml-12',
active && 'opacity-100 ml-0 delay-150',
// level === 'home' && 'ml-12',
// disabled
level !== 'home' && level !== id ? '-ml-8' : '',
level !== id ? 'opacity-0 invisible absolute' : '',
// level !== 'home' && level !== id ? '-ml-8' : '',
!active ? 'opacity-0 invisible absolute' : '',
].join(' ')}
>
<ul className={['relative w-full flex flex-col gap-0'].join(' ')}>
<Link href={`${menu.parent ?? '/'}`} passHref>
<a
className={[
'flex items-center gap-1 text-xs group mb-3',
'text-base transition-all duration-200 text-brand-900 hover:text-brand-1200 hover:cursor-pointer ',
].join(' ')}
>
<div className="relative w-2">
<div className="transition-all ease-out ml-0 group-hover:-ml-1">
<IconChevronLeft size={10} strokeWidth={3} />
</div>
</div>
<span>Back to Home</span>
</a>
</Link>
<Link href={menu.url ?? ''} passHref>
<a>
<div className="flex items-center gap-3 my-3">
<Image
alt={menu.icon}
width={15}
height={15}
src={
`${router.basePath}` +
`/img/icons/menu/${menu.icon}${isDarkMode ? '' : '-light'}.svg`
}
/>
{/* </div> */}
<span
className={[
' ',
!menu.title && 'capitalize',
menu.url === router.pathname
? 'text-brand-900'
: 'hover:text-brand-900 text-scale-1200',
].join(' ')}
>
{menu.title ?? level}
</span>
</div>
</a>
</Link>
{menu.items.map((x, index) => {
// console.log('1st type of link?', x.items && x.items.length > 0)
// console.log()
return (
<div key={x.url}>
{x.items && x.items.length > 0 ? (
<div>
{x.items.map((subItem, subItemIndex) => {
// console.log('subitem', { subItem })
// console.log('router', router)
//console.log('subitem url', subItem.url)
let subItemMenuOpen = false
if (router.asPath.includes(subItem.url)) {
subItemMenuOpen = true
}
const LinkContainer = (props) => {
return (
<Link href={props.url} passHref>
<a className={props.className}>{props.children}</a>
</Link>
)
}
return (
<>
{subItemIndex === 0 && (
<>
<div className="h-px w-full bg-green-500 my-3"></div>
<span className="font-mono text-xs uppercase text-scale-1200 font-medium tracking-wider">
{x.name}
</span>
</>
)}
<Accordion.Item key={subItem.label} value={subItem.url}>
<li key={subItem.name}>
<LinkContainer
url={subItem.url}
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(' ')}
parent={subItem.parent}
>
{subItem.icon && (
<Image
alt={subItem.name + router.basePath}
src={
`${router.basePath}` +
`${subItem.icon}${!isDarkMode ? '-light' : ''}.svg`
}
width={15}
height={15}
/>
)}
{subItem.name}
</LinkContainer>
</li>
{subItem.items && subItem.items.length > 0 && (
<Accordion.Content className="transition data-open:animate-slide-down data-closed:animate-slide-up ml-2">
{subItem.items.map((subSubItem, subSubItemIndex) => {
return (
<li key={subItem.name}>
<Link href={`${subSubItem.url}`} passHref>
<a
className={[
'cursor-pointer transition text-sm',
subSubItem.url === router.pathname
? 'text-brand-900'
: 'hover:text-brand-900 text-scale-1000',
].join(' ')}
>
{subSubItem.name}
</a>
</Link>
</li>
)
})}
</Accordion.Content>
)}
</Accordion.Item>
</>
)
})}
</div>
) : (
<>
<li>
<Link href={`${x.url}`} passHref>
<a
className={[
'cursor-pointer transition text-sm',
x.url === router.pathname
? 'text-brand-900'
: 'hover:text-brand-900 text-scale-1000',
].join(' ')}
>
{x.icon && <img className="w-3" src={`${router.basePath}${x.icon}`} />}
{x.name}
</a>
</Link>
</li>
</>
)}
</div>
)
})}
{menu.extras && (
<>
<div className="h-px w-full bg-green-500 my-3"></div>
<span className="font-mono text-xs uppercase text-scale-1200 font-medium tracking-wider mb-2">
Resources
</span>
</>
)}
{menu.extras?.map((x) => {
return (
<div key={x.name}>
<li>
<Link href={`${x.href}`} passHref>
<a className="cursor-pointer transition text-scale-1100 text-sm hover:text-brand-900 flex gap-3 my-1">
{x.icon && <img className="w-4" src={`${router.basePath}${x.icon}`} />}
{x.name}
</a>
</Link>
</li>
</div>
)
})}
</ul>
<NavigationMenuGuideListItems id={id} />
</Accordion.Root>
)
}
@@ -0,0 +1,213 @@
import { useTheme } from 'common/Providers'
import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/router'
import React from 'react'
import { IconChevronLeft } from '~/../../packages/ui'
import * as NavItems from './NavigationMenu.constants'
import * as Accordion from '@radix-ui/react-accordion'
interface ImPureProps {
// users: Array<User>
}
const HeaderImage = React.memo(function HeaderImage(props: any) {
const router = useRouter()
const { isDarkMode } = useTheme()
return (
<Image
alt={props.icon}
width={15}
height={15}
src={`${router.basePath}` + `/img/icons/menu/${props.icon}${isDarkMode ? '' : '-light'}.svg`}
/>
)
})
const HeaderLink = React.memo(function HeaderLink(props: any) {
const router = useRouter()
return (
<span
className={[
' ',
!props.title && 'capitalize',
props.url === router.pathname ? 'text-brand-900' : 'hover:text-brand-900 text-scale-1200',
].join(' ')}
>
{props.title ?? props.id}
</span>
)
})
const ContentAccordionLink = React.memo(function ContentAccordionLink(props: any) {
const router = useRouter()
const { isDarkMode } = useTheme()
let subItemMenuOpen = false
if (router.asPath.includes(props.subItem.url)) {
subItemMenuOpen = true
}
const LinkContainer = (props) => {
return (
<Link href={props.url} passHref>
<a className={props.className}>{props.children}</a>
</Link>
)
}
return (
<>
{props.subItemIndex === 0 && (
<>
<div className="h-px w-full bg-green-500 my-3"></div>
<span className="font-mono text-xs uppercase text-scale-1200 font-medium tracking-wider">
{props.parent.name}
</span>
</>
)}
<Accordion.Item key={props.subItem.label} value={props.subItem.url}>
<li key={props.subItem.name}>
<LinkContainer
url={props.subItem.url}
className={[
'flex items-center gap-2',
'cursor-pointer transition text-sm',
props.subItem.url === router.pathname
? 'text-brand-900'
: 'hover:text-brand-900 text-scale-1000',
].join(' ')}
parent={props.subItem.parent}
>
{props.subItem.icon && (
<Image
alt={props.subItem.name + router.basePath}
src={
`${router.basePath}` + `${props.subItem.icon}${!isDarkMode ? '-light' : ''}.svg`
}
width={15}
height={15}
/>
)}
{props.subItem.name}
</LinkContainer>
</li>
{props.subItem.items && props.subItem.items.length > 0 && (
<Accordion.Content className="transition data-open:animate-slide-down data-closed:animate-slide-up ml-2">
{props.subItem.items.map((subSubItem) => {
return (
<li key={props.subItem.name}>
<Link href={`${subSubItem.url}`} passHref>
<a
className={[
'cursor-pointer transition text-sm',
subSubItem.url === router.pathname
? 'text-brand-900'
: 'hover:text-brand-900 text-scale-1000',
].join(' ')}
>
{subSubItem.name}
</a>
</Link>
</li>
)
})}
</Accordion.Content>
)}
</Accordion.Item>
</>
)
})
const ContentLink = React.memo(function ContentLink(props: any) {
const router = useRouter()
return (
<li>
<Link href={`${props.url}`} passHref>
<a
className={[
'cursor-pointer transition text-sm',
props.url === router.pathname
? 'text-brand-900'
: 'hover:text-brand-900 text-scale-1000',
].join(' ')}
>
{props.icon && (
<Image
alt={props.icon}
width={12}
height={12}
src={`${router.basePath}${props.icon}`}
/>
)}
{props.name}
</a>
</Link>
</li>
)
})
const Content = (props) => {
const { id } = props
const menu = NavItems[id]
return (
<ul className={['relative w-full flex flex-col gap-0'].join(' ')}>
<Link href={`${menu.parent ?? '/'}`} passHref>
<a
className={[
'flex items-center gap-1 text-xs group mb-3',
'text-base transition-all duration-200 text-brand-900 hover:text-brand-1200 hover:cursor-pointer ',
].join(' ')}
>
<div className="relative w-2">
<div className="transition-all ease-out ml-0 group-hover:-ml-1">
<IconChevronLeft size={10} strokeWidth={3} />
</div>
</div>
<span>Back to Home</span>
</a>
</Link>
<Link href={menu.url ?? ''} passHref>
<a>
<div className="flex items-center gap-3 my-3">
<HeaderImage icon={menu.icon} />
<HeaderLink title={menu.title} url={menu.url} id={id} />
</div>
</a>
</Link>
{menu.items.map((x) => {
return (
<div key={x.name}>
{x.items && x.items.length > 0 ? (
<div>
{x.items.map((subItem, subItemIndex) => {
return (
<ContentAccordionLink
key={subItem.name}
subItem={subItem}
subItemIndex={subItemIndex}
parent={x}
/>
)
})}
</div>
) : (
<ContentLink url={x.url} icon={x.icon} name={x.name} key={x.name} />
)}
</div>
)
})}
</ul>
)
}
export default React.memo(Content)
@@ -1,76 +1,9 @@
import * as Accordion from '@radix-ui/react-accordion'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { IconChevronLeft } from 'ui'
import * as NavItems from './NavigationMenu.constants'
import { find } from 'lodash'
import Image from 'next/image'
import { useTheme } from 'common/Providers'
// import apiCommonSections from '~/../../spec/common-client-libs-sections.json'
import RevVersionDropdown from '~/components/RefVersionDropdown'
import { useMenuActiveRefId, useMenuLevelId } from '~/hooks/useMenuState'
import { RefIdOptions, RefKeyOptions } from './NavigationMenu'
import NavigationMenuRefListItems from './NavigationMenuRefListItems'
const FunctionLink = ({
title,
id,
icon,
library,
slug,
}: {
title: string
name?: string
id: string
icon?: string
product?: string
library: string
slug: string
}) => {
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}/${version ? version + '/' : ''}${slug}`} passHref>
<a
className={[
'cursor-pointer transition text-sm hover:text-brand-900 flex gap-3',
active ? 'text-brand-900' : 'text-scale-1000',
].join(' ')}
>
{icon && <Image width={16} height={16} alt={icon} src={`${router.basePath}${icon}`} />}
{title}
</a>
</Link>
</li>
)
}
const SideMenuTitle = ({ title }: { title: string }) => {
return (
<span className="font-mono text-xs uppercase text-scale-1200 font-medium tracking-wider mb-3">
{title}
</span>
)
}
const Divider = () => {
return <div className="h-px w-full bg-blackA-300 dark:bg-whiteA-300 my-3"></div>
}
import React from 'react'
interface INavigationMenuRefList {
id: RefIdOptions
@@ -80,228 +13,34 @@ interface INavigationMenuRefList {
// the keys of menu items that are allowed to be shown on the side menu
// if undefined, we show all the menu items
allowedClientKeys?: string[]
active: boolean
spec?: any
}
const NavigationMenuRefList: React.FC<INavigationMenuRefList> = ({
id,
lib,
commonSections,
allowedClientKeys,
active,
spec,
}) => {
const router = useRouter()
const { isDarkMode } = useTheme()
let sections = commonSections
const allowedKeys = allowedClientKeys
if (!sections) console.error('no common sections imported')
const menu = NavItems[id]
const databaseFunctions = find(sections, { title: 'Database' })
? find(sections, { title: 'Database' }).items
: []
const authFunctions = find(sections, { title: 'Auth' })
? find(sections, { title: 'Auth' }).items
: []
const filterIds =
databaseFunctions.length > 0
? find(databaseFunctions, {
id: 'using-filters',
}) &&
find(databaseFunctions, {
id: 'using-filters',
})
.items.filter((x) => allowedKeys.includes(x.id))
.map((x) => x.id)
: []
const modifierIds =
databaseFunctions.length > 0
? find(databaseFunctions, {
id: 'using-modifiers',
}) &&
find(databaseFunctions, {
id: 'using-modifiers',
})
.items.filter((x) => allowedKeys.includes(x.id))
.map((x) => x.id)
: []
const authServerIds =
databaseFunctions.length > 0
? find(authFunctions, {
id: 'admin-api',
}) &&
find(authFunctions, {
id: 'admin-api',
}).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
level === id && 'opacity-100 ml-0 delay-150 h-auto',
active && 'opacity-100 ml-0 delay-150 h-auto',
// move menu back to margin-left
level === 'home' && 'ml-12',
// level === 'home' && 'ml-12',
// disabled
level !== 'home' && level !== id ? '-ml-8' : '',
level !== id ? 'opacity-0 invisible absolute h-0 overflow-hidden' : '',
// level !== 'home' && level !== id ? '-ml-8' : '',
!active ? 'opacity-0 invisible absolute h-0 overflow-hidden' : '',
].join(' ')}
>
<div className={'w-full flex flex-col gap-0 sticky top-8'}>
<Link href="/" passHref>
<a
className={[
'flex items-center gap-1 text-xs group mb-3',
'text-base transition-all duration-200 text-scale-1100 hover:text-brand-1200 hover:cursor-pointer ',
].join(' ')}
>
<div className="relative w-2">
<div className="transition-all ease-out ml-0 group-hover:-ml-1">
<IconChevronLeft size={10} strokeWidth={3} />
</div>
</div>
<span>Back to Main Menu</span>
</a>
</Link>
<div className="flex items-center gap-3 my-3">
<Image
alt={id}
width={24}
height={24}
src={`${router.basePath}/img/icons/menu/${menu.icon}${isDarkMode ? '' : '-light'}.svg`}
className="rounded"
/>
<span className={['text-base text-brand-1200 ', !menu.title && 'capitalize'].join(' ')}>
{menu.title}
</span>
<RevVersionDropdown />
</div>
{/* )} */}
<ul className="function-link-list">
{sections.map((fn: any, fnIndex) => {
//
// check if the link is allowed to be displayed
function isFuncNotInLibraryOrVersion(id, type) {
if (id && allowedKeys && !allowedKeys.includes(id) && type !== 'markdown') {
/*
* Remove this menu link from side bar, as it does not exist for either
* this language, or for this lib version
*
* */
return true
} else {
return false
}
}
// run allow check
if (isFuncNotInLibraryOrVersion(fn.id, fn.type)) {
return <></>
}
const RenderLink = (props) => {
const activeAccordianItem = useMenuActiveRefId()
let active = false
const isFilter =
filterIds && filterIds.length > 0 && filterIds.includes(activeAccordianItem)
const isModifier =
modifierIds && modifierIds.length > 0 && modifierIds.includes(activeAccordianItem)
const isAuthServer =
authServerIds &&
authServerIds.length > 0 &&
authServerIds.includes(activeAccordianItem)
if (
(isFilter && !isModifier && !isAuthServer && props.id === 'using-filters') ||
(activeAccordianItem === 'using-filters' && props.id === 'using-filters')
) {
active = true
} else if (
(isModifier && !isFilter && !isAuthServer && props.id === 'using-modifiers') ||
(activeAccordianItem === 'using-modifiers' && props.id === 'using-modifiers')
) {
active = true
} else if (
(isAuthServer && !isFilter && !isModifier && props.id === 'admin-api') ||
(activeAccordianItem === 'admin-api' && props.id === 'admin-api')
) {
active = true
} else {
active = false
}
return (
<Accordion.Root
collapsible
key={props.id + '-accordian-root-for-func-' + fnIndex}
type="single"
value={active ? props.id : ''}
>
<Accordion.Item key={props.id + '-accordian-item'} value={props.id}>
<FunctionLink {...props} library={lib} />
<Accordion.Content
key={props.id + '-sub-items-accordion-container'}
className="transition data-open:animate-slide-down data-closed:animate-slide-up ml-2"
>
{props.items &&
props.items
.filter((item) => allowedKeys.includes(item.id))
.map((item) => {
return <FunctionLink {...item} library={lib} />
})}
</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
)
}
// handle subtitles with subitems
if (!fn.id) {
return (
<>
<Divider />
<SideMenuTitle title={fn.title} />
{fn.items &&
fn.items
//.filter((item) => item.libs && item.libs.includes(lib))
.map((item) => {
// run allow check
if (isFuncNotInLibraryOrVersion(item.id, item.type)) return <></>
return <RenderLink {...item} library={menu.title} />
})}
</>
)
} else {
// handle normal links
return (
<>
<RenderLink {...fn} library={menu.title} />
{fn.items &&
fn.items
//.filter((item) => item.libs.includes(lib))
.map((item) => <RenderLink {...item} library={menu.title} />)}
</>
)
}
})}
</ul>
</div>
<NavigationMenuRefListItems id={id} lib={lib} commonSections={commonSections} spec={spec} />
</div>
)
}
export default NavigationMenuRefList
export default React.memo(NavigationMenuRefList)
@@ -0,0 +1,306 @@
import * as Accordion from '@radix-ui/react-accordion'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { IconChevronLeft } from 'ui'
import * as NavItems from './NavigationMenu.constants'
import { find } from 'lodash'
import Image from 'next/image'
import { useTheme } from 'common/Providers'
import RevVersionDropdown from '~/components/RefVersionDropdown'
import { useMenuActiveRefId } from '~/hooks/useMenuState'
import { RefIdOptions, RefKeyOptions } from './NavigationMenu'
import React, { Fragment } from 'react'
import { generateAllowedClientLibKeys } from '~/lib/refGenerator/helpers'
import { isFuncNotInLibraryOrVersion } from './NavigationMenu.utils'
const HeaderImage = React.memo(function HeaderImage(props: any) {
const router = useRouter()
const { isDarkMode } = useTheme()
return (
<Image
alt={props.icon}
width={15}
height={15}
src={`${router.basePath}` + `/img/icons/menu/${props.icon}${isDarkMode ? '' : '-light'}.svg`}
/>
)
})
const HeaderLink = React.memo(function HeaderLink(props: any) {
const router = useRouter()
return (
<span className={['text-base text-brand-1200 ', !props.title && 'capitalize'].join(' ')}>
{props.title ?? props.id}
</span>
)
})
const FunctionLink = React.memo(function FunctionLink({
title,
id,
icon,
library,
slug,
}: {
title: string
name?: string
id: string
icon?: string
product?: string
library: string
slug: string
}) {
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}/${version ? version + '/' : ''}${slug}`} passHref>
<a
className={[
'cursor-pointer transition text-sm hover:text-brand-900 flex gap-3',
active ? 'text-brand-900' : 'text-scale-1000',
].join(' ')}
>
{icon && <Image width={16} height={16} alt={icon} src={`${router.basePath}${icon}`} />}
{title}
</a>
</Link>
</li>
)
})
const RenderLink = React.memo(function RenderLink(props: any) {
const activeAccordianItem = useMenuActiveRefId()
let active = false
const isFilter = props.filterIds?.includes(activeAccordianItem)
const isModifier = props.modifierIds?.includes(activeAccordianItem)
const isAuthServer = props.authServerIds?.includes(activeAccordianItem)
if (
(isFilter && props.id === 'using-filters') ||
(activeAccordianItem === 'using-filters' && props.id === 'using-filters')
) {
active = true
} else if (
(isModifier && props.id === 'using-modifiers') ||
(activeAccordianItem === 'using-modifiers' && props.id === 'using-modifiers')
) {
active = true
} else if (
(isAuthServer && props.id === 'admin-api') ||
(activeAccordianItem === 'admin-api' && props.id === 'admin-api')
) {
active = true
} else {
active = false
}
return (
<Accordion.Root
collapsible
key={props.id + '-accordian-root-for-func-' + props.index}
type="single"
value={active ? props.id : ''}
>
<Accordion.Item key={props.id + '-accordian-item'} value={props.id}>
<FunctionLink library={props.lib} title={props.title} id={props.id} slug={props.slug} />
<Accordion.Content
key={props.id + '-sub-items-accordion-container'}
className="transition data-open:animate-slide-down data-closed:animate-slide-up ml-2"
>
{props.items &&
props.items
.filter((item) => props.allowedKeys.includes(item.id))
.map((item) => {
return (
<FunctionLink
key={item.title}
library={props.lib}
title={item.title}
id={item.id}
slug={item.slug}
/>
)
})}
</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
)
})
const SideMenuTitle = ({ title }: { title: string }) => {
return (
<span className="font-mono text-xs uppercase text-scale-1200 font-medium tracking-wider mb-3">
{title}
</span>
)
}
const Divider = () => {
return <div className="h-px w-full bg-blackA-300 dark:bg-whiteA-300 my-3"></div>
}
interface INavigationMenuRefList {
id: RefIdOptions
lib: RefKeyOptions
commonSections: any[] // to do type up
// the keys of menu items that are allowed to be shown on the side menu
// if undefined, we show all the menu items
allowedClientKeys?: string[]
spec?: any
}
const Content: React.FC<INavigationMenuRefList> = ({ id, lib, commonSections, spec }) => {
const allowedClientKeys = spec ? generateAllowedClientLibKeys(commonSections, spec) : undefined
let sections = commonSections
const allowedKeys = allowedClientKeys
if (!sections) console.error('no common sections imported')
const menu = NavItems[id]
const databaseFunctions = find(sections, { title: 'Database' })
? find(sections, { title: 'Database' }).items
: []
const authFunctions = find(sections, { title: 'Auth' })
? find(sections, { title: 'Auth' }).items
: []
const filterIds =
databaseFunctions.length > 0
? find(databaseFunctions, {
id: 'using-filters',
}) &&
find(databaseFunctions, {
id: 'using-filters',
})
.items.filter((x) => allowedKeys.includes(x.id))
.map((x) => x.id)
: []
const modifierIds =
databaseFunctions.length > 0
? find(databaseFunctions, {
id: 'using-modifiers',
}) &&
find(databaseFunctions, {
id: 'using-modifiers',
})
.items.filter((x) => allowedKeys.includes(x.id))
.map((x) => x.id)
: []
const authServerIds =
databaseFunctions.length > 0
? find(authFunctions, {
id: 'admin-api',
}) &&
find(authFunctions, {
id: 'admin-api',
}).items.map((x) => x.id)
: []
return (
<div className={'w-full flex flex-col gap-0 sticky top-8'}>
<Link href="/" passHref>
<a
className={[
'flex items-center gap-1 text-xs group mb-3',
'text-base transition-all duration-200 text-scale-1100 hover:text-brand-1200 hover:cursor-pointer ',
].join(' ')}
>
<div className="relative w-2">
<div className="transition-all ease-out ml-0 group-hover:-ml-1">
<IconChevronLeft size={10} strokeWidth={3} />
</div>
</div>
<span>Back to Main Menu</span>
</a>
</Link>
<div className="flex items-center gap-3 my-3">
<HeaderImage icon={menu.icon} />
<HeaderLink title={menu.title} url={menu.url} id={id} />
<RevVersionDropdown />
</div>
<ul className="function-link-list">
{sections.map((fn: any, fnIndex) => {
// run allow check
if (isFuncNotInLibraryOrVersion(fn.id, fn.type, allowedKeys)) {
return <Fragment key={fn.id}></Fragment>
}
// handle subtitles with subitems
return fn.id ? (
<Fragment key={fn.id}>
<RenderLink {...fn} lib={lib} />
{fn.items &&
fn.items
//.filter((item) => item.libs.includes(lib))
.map((item) => (
<RenderLink
{...item}
library={menu.title}
index={fnIndex}
modifierIds={modifierIds}
filterIds={filterIds}
authServerIds={authServerIds}
lib={lib}
allowedKeys={allowedKeys}
/>
))}
</Fragment>
) : (
<Fragment key={fn.title}>
<Divider />
<SideMenuTitle title={fn.title} />
{fn.items &&
fn.items.map((item, i) => {
// run allow check
if (isFuncNotInLibraryOrVersion(item.id, item.type, allowedKeys))
return <Fragment key={item.id + i}></Fragment>
return (
<RenderLink
{...item}
key={item.id + i}
library={menu.title}
index={fnIndex}
modifierIds={modifierIds}
filterIds={filterIds}
authServerIds={authServerIds}
lib={lib}
allowedKeys={allowedKeys}
/>
)
})}
</Fragment>
)
})}
</ul>
</div>
)
}
export default React.memo(Content)
@@ -2,19 +2,7 @@ import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { FC, useEffect, useState } from 'react'
import {
Badge,
Button,
Dropdown,
IconCommand,
IconMenu,
IconMessageSquare,
IconMoon,
IconSearch,
IconSun,
Input,
Listbox,
} from 'ui'
import { Button, IconCommand, IconGitHub, IconMoon, IconSearch, IconSun } from 'ui'
import { SearchButton } from '~/components/DocSearch'
import { REFERENCES } from '~/components/Navigation/Navigation.constants'
import { useTheme } from 'common/Providers'
@@ -54,6 +42,7 @@ const TopNavBarRef: FC = () => {
const key = localStorage.getItem('supabaseDarkMode')
document.documentElement.className = key === 'true' ? 'dark' : ''
document.documentElement.style.colorScheme = key === 'true' ? 'dark' : ''
}
const onSelectVersion = (version: string) => {
@@ -134,7 +123,7 @@ const TopNavBarRef: FC = () => {
target="_blank"
rel="noreferrer noopener"
>
Main site
Supabase.com
</Button>
<Button
type="text"
@@ -146,6 +135,18 @@ const TopNavBarRef: FC = () => {
>
Dashboard
</Button>
<ul className="flex items-center">
<Button
type="text"
as="a"
// @ts-ignore
href="https://github.com/supabase/supabase"
target="_blank"
rel="noreferrer noopener"
>
<IconGitHub size={16} />
</Button>
</ul>
<ul className="flex items-center">
<li className="px-4">
<div className="cursor-pointer" onClick={toggleDarkMode}>
+8
View File
@@ -29,6 +29,7 @@ import CliGlobalFlagsHandler from '~/components/reference/enrichments/cli/CliGlo
import Options from '~/components/Options'
import Param from '~/components/Params'
import Image from 'next/image'
const components = {
Admonition,
@@ -73,6 +74,13 @@ const components = {
CliGlobalFlagsHandler: () => <CliGlobalFlagsHandler />,
Options,
Param,
img: (props: any) => {
return (
<span className={['next-image--dynamic-fill'].join(' ')}>
<Image {...props} className={['rounded-md border'].join(' ')} layout="fill" />
</span>
)
},
}
export default components
@@ -9,6 +9,7 @@ import RefSubLayout from '~/layouts/ref/RefSubLayout'
import { extractTsDocNode, generateParameters } from '~/lib/refGenerator/helpers'
import RefDetailCollapse from '~/components/reference/RefDetailCollapse'
import { Fragment } from 'react'
interface ICommonFunc {
id: string
@@ -32,11 +33,6 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
// gracefully return nothing if function does not exist
if (!item) return <></>
if (item && !item['$ref']) {
//console.warn('🚩 issue with $ref in:', item.id)
}
// console.log(item)
const hasTsRef = item['$ref'] || null
const tsDefinition =
@@ -64,11 +60,7 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
<ReactMarkdown className="text-sm">{item.description}</ReactMarkdown>
</div>
)}
{/* {functionMarkdownContent && (
<div className="prose">
<MDXRemote {...functionMarkdownContent} components={components} />
</div>
)} */}
{item.notes && (
<div className="prose">
<ReactMarkdown className="text-sm">{item.notes}</ReactMarkdown>
@@ -89,25 +81,29 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
})
const paramItem = overide?.length > 0 ? overide[0] : param
return (
<Param {...paramItem}>
<Param {...paramItem} key={param.name}>
{paramItem.subContent && (
<div className="mt-3">
<Options>
{param.subContent.map((param) => {
return (
<>
<Fragment key={param.name + 'subcontent'}>
<Options.Option {...param}>
{param.subContent && (
<Options>
{param.subContent.map((param) => {
return <Options.Option {...param} />
return (
<Options.Option
{...param}
key={param.name + 'subcontent-option'}
/>
)
})}
</Options>
)}
</Options.Option>
</>
</Fragment>
)
})}
</Options>
@@ -150,9 +146,18 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
return (
<Tabs.Panel
id={example.id}
key={example.id}
label={example.name}
className="flex flex-col gap-3"
>
{example.description && (
<div className="prose">
<ReactMarkdown className="text-sm">
{example.description}
</ReactMarkdown>
</div>
)}
{((tables && tables.length > 0) || sql) && (
<RefDetailCollapse
id={`${example.id}-${exampleIndex}-data`}
@@ -203,6 +208,7 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
.replace('js', '')
.replace('ts', ''))}
</CodeBlock>
{response && (
<RefDetailCollapse
id={`${example.id}-${exampleIndex}-response`}
+25
View File
@@ -0,0 +1,25 @@
import { NextSeo } from 'next-seo'
import { useRouter } from 'next/router'
function RefSEO({ title }) {
const router = useRouter()
const path = router.asPath.replace('crawlers/', '')
return (
<NextSeo
title={title}
openGraph={{
title,
url: `https://supabase.com/docs${path}`,
images: [
{
url: `https://supabase.com/docs/img/supabase-og-image.png`,
},
],
}}
/>
)
}
export default RefSEO
@@ -5,7 +5,7 @@ import { useEffect } from 'react'
import components from '~/components'
import RefEducationSection from '~/components/reference/RefEducationSection'
import RefFunctionSection from '~/components/reference/RefFunctionSection'
import OldLayout from '~/layouts/Default'
import RefSubLayout from '~/layouts/ref/RefSubLayout'
import ApiOperationSection from './ApiOperationSection'
import CliCommandSection from './CLICommandSection'
@@ -34,30 +34,19 @@ const RefSectionHandler = (props) => {
}
})
/*
* handle old ref pages
*/
if (!isNewDocs) {
return (
// @ts-ignore
<OldLayout meta={props.pageProps.meta} toc={props.pageProps.toc}>
<MDXRemote {...props.pageProps.content} components={components} />
</OldLayout>
)
}
return (
<RefSubLayout>
{props.sections.map((x) => {
{props.sections.map((x, i) => {
switch (x.type) {
case 'markdown':
const markdownData = props.pageProps.docs.find((doc) => doc.id === x.id)
return <RefEducationSection item={x} markdownContent={markdownData} />
return <RefEducationSection key={x.id + i} item={x} markdownContent={markdownData} />
break
case 'function':
return (
<RefFunctionSection
key={x.id + i}
funcData={x}
commonFuncData={x}
spec={props.spec}
@@ -65,13 +54,21 @@ const RefSectionHandler = (props) => {
/>
)
case 'cli-command':
return <CliCommandSection funcData={x} commonFuncData={x} />
return <CliCommandSection key={x.id + i} funcData={x} commonFuncData={x} />
break
case 'operation':
return <ApiOperationSection funcData={x} commonFuncData={x} spec={props.spec} />
return (
<ApiOperationSection
key={x.id + i}
funcData={x}
commonFuncData={x}
spec={props.spec}
/>
)
default:
return (
<RefFunctionSection
key={x.id + i}
funcData={x}
commonFuncData={x}
spec={props.spec}
+6 -146
View File
@@ -1,150 +1,10 @@
[
{ "title": "Contributing", "url": "/handbook/contributing" },
{ "title": "Changelog", "url": "https://supabase.com/changelog" },
{
"title": "Product",
"links": [
{
"text": "Database",
"url": "/database"
},
{
"text": "Auth",
"url": "/auth"
},
{
"text": "Functions",
"url": "/edge-functions"
},
{
"text": "Realtime",
"url": "/realtime"
},
{
"text": "Storage",
"url": "/storage"
},
{
"text": "Pricing",
"url": "/pricing"
},
{
"text": "Beta",
"url": "/beta"
}
]
"title": "Author Styleguide",
"url": "https://github.com/supabase/supabase/blob/master/DEVELOPERS.md"
},
{
"title": "Resources",
"links": [
{
"text": "Support",
"url": "/support"
},
{
"text": "System Status",
"url": "https://status.supabase.com/"
},
{
"text": "Integrations",
"url": "/partners/integrations"
},
{
"text": "Experts",
"url": "/partners/experts"
},
{
"text": "Brand Assets / Logos",
"url": "/brand-assets"
},
{
"text": "DPA",
"url": "/legal/dpa"
},
{
"text": "SOC2",
"url": "/legal/soc2"
}
]
},
{
"title": "Developers",
"links": [
{
"text": "Documentation",
"url": "/docs"
},
{
"text": "API Reference",
"url": "/docs/reference"
},
{
"text": "Changelog",
"url": "/changelog"
},
{
"text": "Contributing",
"url": "/docs/handbook/contributing"
},
{
"text": "Open Source",
"url": "/docs/oss"
},
{
"text": "SupaSquad",
"url": "/supasquad"
},
{
"text": "DevTo",
"url": "https://dev.to/supabase"
},
{
"text": "RSS",
"url": "/rss.xml"
}
]
},
{
"title": "Company",
"links": [
{
"text": "Blog",
"url": "/blog"
},
{
"text": "Careers",
"url": "https://about.supabase.com/careers"
},
{
"text": "Company",
"url": "/company"
},
{
"text": "Terms of Service",
"url": "/docs/company/terms"
},
{
"text": "Privacy Policy",
"url": "/docs/company/privacy"
},
{
"text": "Acceptable Use Policy",
"url": "/docs/company/aup"
},
{
"text": "Service Level Agreement",
"url": "/docs/company/sla"
},
{
"text": "Humans.txt",
"url": "/humans.txt"
},
{
"text": "Lawyers.txt",
"url": "/lawyers.txt"
},
{
"text": "Security.txt",
"url": "/.well-known/security.txt"
}
]
}
{ "title": "Open Source", "url": "https://supabase.com/docs/oss" },
{ "title": "SupaSquad", "url": "https://supabase.com/supasquad" }
]
+1 -1
View File
@@ -7,7 +7,7 @@ export const menuState = proxy({
setMenuActiveRefId: (value) => {
menuState.menuActiveRefId = value
},
menuLevelId: '',
menuLevelId: 'home',
setMenuLevelId: (value) => {
menuState.menuMobileOpen = false
menuState.menuLevelId = value
-94
View File
@@ -1,94 +0,0 @@
import { useEffect, FC } from 'react'
import { NextSeo } from 'next-seo'
import NavBar from '../components/Navigation/NavBar'
import SideBar from '../components/Navigation/SideBar'
import Footer from '../components/Footer'
import TableOfContents from '~/components/TableOfContents'
import { menuItems } from '~/components/Navigation/Navigation.constants'
import { useRouter } from 'next/router'
import { getPageType } from '~/lib/helpers'
interface Props {
meta: { title: string; description?: string; hide_table_of_contents?: boolean }
children: any
toc?: any
menuItems?: any
currentPage?: string
}
const Layout: FC<Props> = ({ meta, children, toc }) => {
const { asPath } = useRouter()
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 hasTableOfContents =
toc !== undefined && toc.json.filter((item) => item.lvl !== 1 && item.lvl <= 3).length > 0
const pageType = getPageType(asPath)
console.log('Default, pageType', pageType)
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 />
<div className="flex w-full flex-row">
<SideBar menuItems={menuItems[pageType]} />
<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
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]`}
>
{children}
</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(' ')}
>
<TableOfContents toc={toc} />
</div>
)}
</div>
</div>
<Footer />
</main>
</>
)
}
export default Layout
+296 -281
View File
@@ -1,320 +1,335 @@
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 FooterHelpCallout from '~/components/FooterHelpCallout'
import { memo, useEffect } from 'react'
import Footer from '~/components/Navigation/Footer'
import { menuState, useMenuLevelId, useMenuMobileOpen } from '~/hooks/useMenuState'
import { useEffect } from 'react'
const SiteRefLayout = ({ children }) => {
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',
},
self_hosting: {
icon: '/docs/img/icons/menu/self-hosting',
name: 'Self-Hosting',
},
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',
},
}
const MobileHeader = memo(function MobileHeader() {
const mobileMenuOpen = useMenuMobileOpen()
const menuLevel = useMenuLevelId()
return (
<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>
)
})
const MobileMenuBackdrop = memo(function MobileMenuBackdrop() {
const mobileMenuOpen = useMenuMobileOpen()
return (
<div
className={[
'h-full',
'left-0',
'right-0',
'z-10',
'backdrop-blur-sm backdrop-filter bg-white-1200 dark:bg-blackA-600',
mobileMenuOpen ? 'absolute h-full w-full top-0 left-0' : 'hidden h-0',
// always hide on desktop
'lg:hidden',
].join(' ')}
onClick={() => menuState.setMenuMobileOpen(!mobileMenuOpen)}
></div>
)
})
const SideMenu = memo(function SideMenu() {
return (
<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',
// desktop styles
'lg:relative lg:top-0 lg:left-0 lg:pb-10 lg:px-10 lg:pt-0 lg:flex',
'lg:opacity-100 lg:visible',
].join(' ')}
>
<NavigationMenu />
</div>
)
})
const HeaderLogo = memo(function HeaderLogo() {
const { isDarkMode } = useTheme()
return (
<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>
)
})
const Container = memo(function Container(props) {
const mobileMenuOpen = useMenuMobileOpen()
return (
<div
// #docs-content-container is used by layout to scroll to top
id="docs-content-container"
className={[
// 'overflow-x-auto',
'w-full h-screen transition-all ease-out',
'absolute lg:relative',
mobileMenuOpen
? '!w-auto ml-[75%] sm:ml-[50%] md:ml-[33%] overflow-hidden'
: 'overflow-auto',
// desktop override any margin styles
'lg:ml-0',
].join(' ')}
>
<div className="flex flex-col relative">{props.children}</div>
</div>
)
})
const NavContainer = memo(function NavContainer() {
const mobileMenuOpen = useMenuMobileOpen()
return (
<div
className={[
// 'hidden',
'absolute lg:relative',
mobileMenuOpen ? 'w-[75%] sm:w-[50%] md:w-[33%] left-0' : 'w-0 -left-[280px]',
'lg:w-[420px] !lg:left-0',
// desktop override any left styles
'lg:left-0',
'transition-all',
'top-0',
'h-screen',
'flex flex-col ml-0',
].join(' ')}
>
<div
className={[
'top-0',
'relative',
'w-auto',
'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 lg:flex lg:height-auto',
'pt-8 bg-scale-200 flex-col gap-8',
].join(' ')}
>
<HeaderLogo />
</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>
<SideMenu />
</div>
</div>
)
})
const SiteLayout = ({ children }) => {
// const mobileMenuOpen = useMenuMobileOpen()
useEffect(() => {
const key = localStorage.getItem('supabaseDarkMode')
if (!key) {
// Default to dark mode if no preference config
document.documentElement.className = 'dark'
document.documentElement.style.colorScheme = 'dark'
} else {
document.documentElement.className = key === 'true' ? 'dark' : ''
document.documentElement.style.colorScheme = key === 'true' ? 'dark' : ''
}
}, [])
const menuLevel = useMenuLevelId()
const mobileMenuOpen = useMenuMobileOpen()
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>
{/* <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={[
'hidden', // experiment hidden
'transition-all',
'top-0',
'relative',
'h-screen w-[420px]',
'z-10',
'lg:flex flex-col ml-0',
].join(' ')}
>
<NavContainer />
<Container>
<div className={['lg:sticky top-0 z-10 overflow-hidden'].join(' ')}>
<TopNavBarRef />
</div>
<div
className={[
'top-0',
'relative',
'w-auto',
'sticky transition-all top-0',
'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 className={['lg:hidden', 'px-5 ', 'border-b z-10'].join(' ')}>
<MobileHeader />
</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/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="grow px-5 max-w-7xl mx-auto py-16">
{children}
<FooterHelpCallout />
<Footer />
</div>
</div>
<MobileMenuBackdrop />
</Container>
</div>
</main>
)
}
export default SiteRefLayout
export default SiteLayout
-321
View File
@@ -1,321 +0,0 @@
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 FooterHelpCallout from '~/components/FooterHelpCallout'
import { menuState } from '~/hooks/useMenuState'
import { useEffect } from 'react'
const SiteRefLayout = ({ children }) => {
const { isDarkMode } = useTheme()
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 menuLevel = 'home'
// useMenuLevelId()
const mobileMenuOpen = false
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>
{/* <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={[
'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>
<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 SiteRefLayout
+26 -14
View File
@@ -31,10 +31,11 @@ const Layout: FC<Props> = (props) => {
const [tocList, setTocList] = useState([])
const { asPath } = useRouter()
const page = getPageType(asPath)
const router = useRouter()
const EDIT_BUTTON_EXCLUDE_LIST = ['/404']
useEffect(() => {
if (hash && tocList.length > 0) {
highlightSelectedTocItem(hash as string)
@@ -59,6 +60,8 @@ const Layout: FC<Props> = (props) => {
const hasTableOfContents = tocList.length > 0
const ogPageType = asPath.split('/')[2]
return (
<>
<NextSeo
@@ -70,13 +73,17 @@ const Layout: FC<Props> = (props) => {
url: `https://supabase.com/docs${asPath}`,
images: [
{
url: `https://supabase.com/docs/img/supabase-og-image.png`,
url: `https://obuldanrptloktxcffvn.functions.supabase.co/og-images?site=docs${
ogPageType ? `&type=${ogPageType}` : ''
}&title=${encodeURIComponent(props.meta?.title)}&description=${encodeURIComponent(
props.meta?.description
)}`,
},
],
}}
/>
<div className={['grid grid-cols-12 relative'].join(' ')}>
<div className={['grid grid-cols-12 relative gap-4'].join(' ')}>
<div
className={[
'relative',
@@ -100,18 +107,23 @@ const Layout: FC<Props> = (props) => {
)}
<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>
<div className="mt-16 not-prose">
<div>
<Link
href={`https://github.com/supabase/supabase/edit/master/apps/docs/pages${router.asPath}.mdx`}
passHref
>
<a className="text-sm transition flex items-center gap-1 text-scale-1000 hover:text-scale-1200">
Edit this page on GitHub <IconExternalLink size={14} strokeWidth={1.5} />
</a>
</Link>
{EDIT_BUTTON_EXCLUDE_LIST.includes(router.route) ? (
<></>
) : (
<div className="mt-16 not-prose">
<div>
<Link
href={`https://github.com/supabase/supabase/edit/master/apps/docs/pages${router.asPath}.mdx`}
passHref
>
<a className="text-sm transition flex items-center gap-1 text-scale-1000 hover:text-scale-1200">
Edit this page on GitHub <IconExternalLink size={14} strokeWidth={1.5} />
</a>
</Link>
</div>
</div>
</div>
)}
</article>
</div>
{!props.hideToc && hasTableOfContents && !props.meta?.hide_table_of_contents && (
+25 -17
View File
@@ -4,6 +4,7 @@ import { highlightSelectedNavItem } from '~/components/CustomHTMLElements/Custom
import { useRouter } from 'next/router'
import { useNavigationMenuContext } from '~/components/Navigation/NavigationMenu/NavigationMenu.Context'
import { menuState } from '~/hooks/useMenuState'
import Image from 'next/image'
interface ISectionContainer {
id: string
@@ -54,10 +55,9 @@ const RefSubLayout: FC<RefSubLayoutType> & RefSubLayoutSubComponents = (props) =
}
const Section: FC<ISectionContainer> = (props) => {
// console.log({ props })
return (
<article
key={props.id}
key={props.id + 'section'}
className={[
props.singleColumn ? 'prose dark:prose-dark w-full' : 'w-full',
'py-16 lg:py-32 first:pt-8 last:pb-8',
@@ -77,8 +77,13 @@ const Section: FC<ISectionContainer> = (props) => {
const StickyHeader: FC<StickyHeader> = ({ icon, ...props }) => {
const router = useRouter()
const { setActiveRefItem } = useNavigationMenuContext()
// we're serving search bots a different file (/crawlers/[...slug])
// and need to modify content to suit that
const isCrawlerPage = router.route.includes('/crawlers/[...slug]')
const { ref } = useInView({
threshold: 1,
rootMargin: '30% 0% -35% 0px',
@@ -99,21 +104,25 @@ const StickyHeader: FC<StickyHeader> = ({ icon, ...props }) => {
<div className={['flex items-center gap-3 not-prose', icon && 'mb-8'].join(' ')}>
{icon && (
<div className="w-8 h-8 bg-brand-500 rounded flex items-center justify-center">
<img className="w-6 h-6" src={`${icon}.svg`} />
<Image width={16} height={16} alt={icon} src={`${icon}.svg`} />
</div>
)}
<h2
ref={ref}
id={props.slug}
data-ref-id={props.id}
className={[
'text-2xl font-medium text-scale-1200 scroll-mt-24',
!icon && 'mb-8',
props.monoFont && 'font-mono',
].join(' ')}
>
{props.title && <span className="max-w-xl">{props.title}</span>}
</h2>
{isCrawlerPage ? (
<h1>{props.title}</h1>
) : (
<h2
ref={ref}
id={props.slug}
data-ref-id={props.id}
className={[
'text-2xl font-medium text-scale-1200 scroll-mt-24',
!icon && 'mb-8',
props.monoFont && 'font-mono',
].join(' ')}
>
{props.title && <span className="max-w-xl">{props.title}</span>}
</h2>
)}
</div>
)
}
@@ -139,10 +148,9 @@ const EducationRow: FC<IEducationRow> = (props) => {
}
const EducationSection: FC<IEducationSection> = ({ icon, hideTitle = false, ...props }) => {
// console.log({ props })
return (
<article
key={props.id}
key={props.id + 'education'}
className={'prose dark:prose-dark max-w-none py-16 lg:py-32 first:pt-8 last:pb-8'}
>
{!hideTitle && <StickyHeader {...props} icon={icon} />}
@@ -2,7 +2,6 @@ import { MDXProvider } from '@mdx-js/react'
import Head from 'next/head'
import { FC, useEffect, useState } from 'react'
import components from '~/components'
import Footer from '~/components/Footer'
import SideBar from '~/components/Navigation/SideBar'
import TableOfContents from '~/components/TableOfContents'
+20
View File
@@ -0,0 +1,20 @@
// @ts-expect-error
import specStorageV0 from '~/../../spec/storage_v0_config.yaml' assert { type: 'yml' }
// @ts-expect-error
import specRealtimeV0 from '~/../../spec/realtime_v0_config.yaml' assert { type: 'yml' }
// @ts-expect-error
import specAuthV1 from '~/../../spec/gotrue_v1_config.yaml' assert { type: 'yml' }
function getStorageConfigV0() {
return { ...specStorageV0 }
}
function getRealtimeConfigV0() {
return { ...specRealtimeV0 }
}
function getAuthConfigV1() {
return { ...specAuthV1 }
}
export { getStorageConfigV0, getRealtimeConfigV0, getAuthConfigV1 }
+21
View File
@@ -2,6 +2,7 @@ import { TsDoc } from '../../generator/legacy/definitions'
import { values, mapValues } from 'lodash'
import { OpenAPIV3 } from 'openapi-types'
import { flattenSections } from '../helpers'
export function extractTsDocNode(nodeToFind: string, definition: any) {
const nodePath = nodeToFind.split('.')
@@ -285,3 +286,23 @@ export const toArrayWithKey = (obj: object, keyAs: string) =>
return value
})
)
export function generateAllowedClientLibKeys(sections, spec) {
// Filter parent sections first
const specIds = spec.functions.map((func) => {
return func.id
})
const newShape = flattenSections(sections).filter((section) => {
if (specIds.includes(section.id)) {
return section
}
})
const final = newShape.map((func) => {
return func.id
})
return final
}
+6 -1
View File
@@ -48,7 +48,12 @@ const nextConfig = {
basePath: '/docs',
images: {
dangerouslyAllowSVG: true,
domains: ['avatars.githubusercontent.com', 'github.com', 'user-images.githubusercontent.com'],
domains: [
'avatars.githubusercontent.com',
'github.com',
'user-images.githubusercontent.com',
'raw.githubusercontent.com',
],
},
experimental: {
mdxRs: true,
-52
View File
@@ -1,52 +0,0 @@
import { useEffect, useState } from 'react'
import Link from 'next/link'
import { Button } from 'ui'
import Head from 'next/head'
export default function Custom404() {
const [show404, setShow404] = useState<boolean>(false)
useEffect(() => {
setTimeout(() => {
setShow404(true)
}, 500)
}, [])
return (
<>
<Head>
<title>404 | Supabase</title>
</Head>
<div className="relative mx-auto flex h-screen w-full flex-col items-center justify-center">
<div className="absolute">
<h1
className={`text-scale-1200 select-none text-[14rem] opacity-[5%] filter transition duration-200 sm:text-[18rem] lg:text-[28rem] ${
show404 ? 'blur-sm' : 'blur-none'
}`}
>
404
</h1>
</div>
<div
className={`flex flex-col items-center justify-center space-y-6 transition ${
show404 ? 'opacity-100' : 'opacity-0'
}`}
>
<div className="text-scale-1200 flex w-[320px] flex-col items-center justify-center space-y-3">
<h1 className="m-2 text-2xl">Looking for something?</h1>
<p className="text-center text-sm">
We could not find the page that you are looking for!
</p>
</div>
<div className="flex items-center space-x-4">
<Link href="/">
<Button as="a" size="small" className="text-white">
Head back
</Button>
</Link>
</div>
</div>
</div>
</>
)
}
-74
View File
@@ -1,74 +0,0 @@
import toc from 'markdown-toc'
import { MDXRemote } from 'next-mdx-remote'
import { serialize } from 'next-mdx-remote/serialize'
import components from '../components/index'
import NewLayout from '~/layouts/DefaultGuideLayout'
import Layout from '~/layouts/Default'
import { getAllDocs, getDocsBySlug } from '../lib/docs'
interface Meta {
id: string
title: string
sidebar_label: string
hide_table_of_contents: boolean
}
interface Props {
meta: Meta
content: any
toc: any
}
const isNewDocs = process.env.NEXT_PUBLIC_NEW_DOCS === 'true'
export default function Doc({ meta, content, toc }: Props) {
return (
// @ts-ignore
<>
{Object.entries(meta).length > 0 || !isNewDocs ? (
<Layout meta={meta} toc={toc}>
<MDXRemote {...content} components={components} />
</Layout>
) : (
<NewLayout meta={meta} toc={toc}>
<MDXRemote {...content} components={components} />
</NewLayout>
)}
</>
)
}
export async function getStaticProps({ params }: { params: { slug: string[] } }) {
let slug
if (params.slug.length > 1) {
slug = `docs/${params.slug.join('/')}`
} else {
slug = `docs/${params.slug[0]}`
}
let doc = getDocsBySlug(slug)
const content = await serialize(doc.content || '')
return {
props: {
...doc,
content,
toc: toc(doc.content, { maxdepth: 1, firsth1: false }),
},
}
}
export function getStaticPaths() {
let docs = getAllDocs()
return {
paths: docs.map(() => {
return {
params: {
slug: docs.map((d) => d.slug),
},
}
}),
fallback: 'blocking',
}
}
-1
View File
@@ -13,7 +13,6 @@ import '../styles/main.scss?v=1.0.0'
import '../styles/new-docs.scss'
import '../styles/prism-okaidia.scss'
import { post } from '~/lib/fetchWrappers'
import { menuState } from '~/hooks/useMenuState'
function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const router = useRouter()
+1 -1
View File
@@ -16,7 +16,7 @@ Self-hosting Supabase is free. If you wish to use our cloud-platform, we provide
### How do I host Supabase?
You can use the docker-compose script [here](https://github.com/supabase/supabase/tree/master/docker), and find detailed instructions [here](/docs/guides/hosting/overview).
You can use the docker compose script [here](https://github.com/supabase/supabase/tree/master/docker), and find detailed instructions [here](/docs/guides/hosting/overview).
Supabase is an amalgamation of open source tools. Some of these tools are made by Supabase (like our [Realtime Server](https://github.com/supabase/realtime)), some we support directly (like [PostgREST](http://postgrest.org/en/v7.0.0/)), and some are third-party tools (like [KonSupabase is an amalgamation open sourceg](https://github.com/Kong/kong)).
+1 -1
View File
@@ -62,7 +62,7 @@ A scalable websocket engine for managing user Presence, broadcasting messages, a
- Official Docs: [Supabase Realtime docs](/docs/guides/realtime)
- Source code: [github.com/supabase/realtime](https://github.com/supabase/realtime)
- License: [Apache 2](https://github.com/supabase/realtime/blob/master/LICENSE)
- License: [Apache 2](https://github.com/supabase/realtime/blob/main/LICENSE)
- Language: Elixir
### Storage API (large file storage)
@@ -83,9 +83,9 @@ Wrap your `pages/_app.js` component with the `SessionContextProvider` component:
```jsx title=pages/_app.js
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider } from '@supabase/auth-helpers-react'
import { useState } from 'react'
function MyApp({ Component, pageProps }) {
const router = useRouter()
// Create a new supabase browser client on every first render.
const [supabaseClient] = useState(() => createBrowserSupabaseClient())
@@ -108,6 +108,7 @@ Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component
```tsx lines=2,8 title=pages/_app.tsx
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider, Session } from '@supabase/auth-helpers-react'
import { useState } from 'react'
function MyApp({
Component,
+1 -1
View File
@@ -55,7 +55,7 @@ You can enable third-party providers with the click of a button by navigating to
### Redirect URLs and wildcards
When using third-party providers, the [Supabase client library](/docs/reference/javascript/auth-signinwithoauth#sign-in-using-a-third-party-provider-with-redirect) redirects the user to the provider. When the third-party provider successfully authenticates the user, the provider redirects the user to the Supabase Auth callback URL where they are further redirected to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/reference/auth/config#site_url). You can modify the `SITE_URL` or [add additional redirect URLs](https://app.supabase.com/project/_/auth/settings).
When using third-party providers, the [Supabase client library](/docs/reference/javascript/auth-signinwithoauth#sign-in-using-a-third-party-provider-with-redirect) redirects the user to the provider. When the third-party provider successfully authenticates the user, the provider redirects the user to the Supabase Auth callback URL where they are further redirected to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/reference/auth/config#site_url). You can modify the `SITE_URL` or add additional [redirect URLs](https://app.supabase.com/project/_/auth/url-configuration).
You can use wildcard match patterns to support preview URLs from providers like Netlify and Vercel. See the [full list of supported patterns](https://pkg.go.dev/github.com/gobwas/glob#Compile). Use [this tool](https://www.digitalocean.com/community/tools/glob?comments=true&glob=http%3A%2F%2Flocalhost%3A3000%2F%2A%2A&matches=false&tests=http%3A%2F%2Flocalhost%3A3000&tests=http%3A%2F%2Flocalhost%3A3000%2F&tests=http%3A%2F%2Flocalhost%3A3000%2F%3Ftest%3Dtest&tests=http%3A%2F%2Flocalhost%3A3000%2Ftest-test%3Ftest%3Dtest&tests=http%3A%2F%2Flocalhost%3A3000%2Ftest%2Ftest%3Ftest%3Dtest) to test your patterns.
+1 -1
View File
@@ -29,7 +29,7 @@ Check out the [Edge Function Examples](https://github.com/supabase/supabase/tree
<div className="grid md:grid-cols-12 gap-4 not-prose">
{examples.map((x) => (
<div className="col-span-4">
<div className="col-span-4" key={x.href}>
<Link href={x.href} passHref>
<a>
<GlassPanel icon={'/docs/img/icons/github-icon'} hasLightIcon={true} title={x.name}>
@@ -63,7 +63,7 @@ A scalable websocket engine for managing user Presence, broadcasting messages, a
- Official Docs: [Supabase Realtime docs](/docs/guides/realtime)
- Source code: [github.com/supabase/realtime](https://github.com/supabase/realtime)
- License: [Apache 2](https://github.com/supabase/realtime/blob/master/LICENSE)
- License: [Apache 2](https://github.com/supabase/realtime/blob/main/LICENSE)
- Language: Elixir
### Storage API (large file storage)
@@ -125,6 +125,7 @@ export default MyApp
Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component:
```jsx lines=2,8 title=pages/_app.tsx
import { useState } from 'react'
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider, Session } from '@supabase/auth-helpers-react'
+2 -1
View File
@@ -1,5 +1,6 @@
import Layout from '~/layouts/DefaultLayout'
import Link from 'next/link'
import Image from 'next/image'
import { GlassPanel } from 'ui'
import { integrations } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
@@ -20,7 +21,7 @@ Explore a variety of integrations from Supabase partners. Need a different integ
<GlassPanel
title={integration.name}
background={false}
icon={<img className="w-10 h-10 rounded" src={`/docs/img/integrations/logos/${integration.name.toLowerCase()}_logo.png`}/>}
icon={<Image height={40} width={40} className="rounded" src={`/docs/img/integrations/logos/${integration.name.toLowerCase()}_logo.png`}/>}
>
{integration.description}
+154 -186
View File
@@ -61,7 +61,7 @@ cd supabase-authsignal-example
Create a `.env.local` file and enter the following values:
```
```sh
NEXT_PUBLIC_SUPABASE_URL=get-from-supabase-dashboard
NEXT_PUBLIC_SUPABASE_ANON_KEY=get-from-supabase-dashboard
AUTHSIGNAL_SECRET=get-from-authsignal-dashboard
@@ -84,19 +84,19 @@ npm run dev
Install the Supabase client and Auth helpers for Next.js:
```
```bash
npm install @supabase/supabase-js @supabase/auth-helpers-nextjs
```
Install the Authsignal Node.js client:
```
```bash
npm install @authsignal/node
```
Finally install 2 packages to help encrypt and serialize session data in cookies:
```
```bash
npm install @hapi/iron cookie
npm install --save-dev @types/cookie
```
@@ -105,18 +105,18 @@ npm install --save-dev @types/cookie
Add the following code to `/lib/authsignal.ts`:
```
import { Authsignal } from "@authsignal/node";
```ts
import { Authsignal } from '@authsignal/node'
const secret = process.env.AUTHSIGNAL_SECRET;
const secret = process.env.AUTHSIGNAL_SECRET
if (!secret) {
throw new Error("AUTHSIGNAL_SECRET is undefined");
throw new Error('AUTHSIGNAL_SECRET is undefined')
}
const redirectUrl = "http://localhost:3000/api/callback";
const redirectUrl = 'http://localhost:3000/api/callback'
export const authsignal = new Authsignal({ secret, redirectUrl });
export const authsignal = new Authsignal({ secret, redirectUrl })
```
The `redirectUrl` here is a Next.js API route which Authsignal will redirect back to after an MFA challenge. We'll implement this below.
@@ -131,83 +131,72 @@ Next we will add some helper functions for managing cookies:
Add the following code to `/lib/cookies.ts`:
```
import Iron from "@hapi/iron";
import { Session } from "@supabase/supabase-js";
import { parse, serialize } from "cookie";
import { NextApiRequest, NextApiResponse } from "next";
```ts
import Iron from '@hapi/iron'
import { Session } from '@supabase/supabase-js'
import { parse, serialize } from 'cookie'
import { NextApiRequest, NextApiResponse } from 'next'
export async function setTempCookie(session: Session, res: NextApiResponse) {
const token = await Iron.seal(session, TEMP_TOKEN_SECRET, Iron.defaults);
const token = await Iron.seal(session, TEMP_TOKEN_SECRET, Iron.defaults)
const cookie = serialize(TEMP_COOKIE, token, {
maxAge: session.expires_in,
httpOnly: true,
secure: process.env.NODE_ENV === "production",
path: "/",
sameSite: "lax",
});
secure: process.env.NODE_ENV === 'production',
path: '/',
sameSite: 'lax',
})
res.setHeader("Set-Cookie", cookie);
res.setHeader('Set-Cookie', cookie)
}
export async function getSessionFromTempCookie(
req: NextApiRequest
): Promise<Session | undefined> {
const cookie = req.headers.cookie as string;
export async function getSessionFromTempCookie(req: NextApiRequest): Promise<Session | undefined> {
const cookie = req.headers.cookie as string
const cookies = parse(cookie ?? "");
const cookies = parse(cookie ?? '')
const tempCookie = cookies[TEMP_COOKIE];
const tempCookie = cookies[TEMP_COOKIE]
if (!tempCookie) {
return undefined;
return undefined
}
const session = await Iron.unseal(
tempCookie,
TEMP_TOKEN_SECRET,
Iron.defaults
);
const session = await Iron.unseal(tempCookie, TEMP_TOKEN_SECRET, Iron.defaults)
return session;
return session
}
export function setAuthCookie(session: Session, res: NextApiResponse) {
const { access_token, refresh_token, expires_in } = session;
const { access_token, refresh_token, expires_in } = session
const authCookies = [
{ name: ACCESS_TOKEN_COOKIE, value: access_token },
refresh_token
? { name: REFRESH_TOKEN_COOKIE, value: refresh_token }
: undefined,
refresh_token ? { name: REFRESH_TOKEN_COOKIE, value: refresh_token } : undefined,
]
.filter(isDefined)
.map(({ name, value }) =>
serialize(name, value, {
maxAge: expires_in,
httpOnly: true,
secure: process.env.NODE_ENV === "production",
path: "/",
sameSite: "lax",
secure: process.env.NODE_ENV === 'production',
path: '/',
sameSite: 'lax',
})
);
)
// Also clear the temp cookie
const updatedCookies = [
...authCookies,
serialize(TEMP_COOKIE, "", { maxAge: -1, path: "/" }),
];
const updatedCookies = [...authCookies, serialize(TEMP_COOKIE, '', { maxAge: -1, path: '/' })]
res.setHeader("Set-Cookie", updatedCookies);
res.setHeader('Set-Cookie', updatedCookies)
}
const isDefined = <T>(value: T | undefined): value is T => !!value;
const isDefined = <T>(value: T | undefined): value is T => !!value
const TEMP_TOKEN_SECRET = process.env.TEMP_TOKEN_SECRET!;
const TEMP_COOKIE = "as-mfa-cookie";
const ACCESS_TOKEN_COOKIE = "sb-access-token";
const REFRESH_TOKEN_COOKIE = "sb-refresh-token";
const TEMP_TOKEN_SECRET = process.env.TEMP_TOKEN_SECRET!
const TEMP_COOKIE = 'as-mfa-cookie'
const ACCESS_TOKEN_COOKIE = 'sb-access-token'
const REFRESH_TOKEN_COOKIE = 'sb-refresh-token'
```
## Step 7: Building the UI
@@ -216,34 +205,34 @@ We will add some form components for signing in and signing up as well as a basi
Add the following code to `/pages/sign-up.tsx`:
```
import Link from "next/link";
import { useRouter } from "next/router";
```ts
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function SignUpPage() {
const router = useRouter();
const router = useRouter()
return (
<main>
<form
onSubmit={async (e) => {
e.preventDefault();
e.preventDefault()
const target = e.target as typeof e.target & {
email: { value: string };
password: { value: string };
};
email: { value: string }
password: { value: string }
}
const email = target.email.value;
const password = target.password.value;
const email = target.email.value
const password = target.password.value
await fetch("/api/sign-up", {
method: "POST",
headers: { "Content-Type": "application/json" },
await fetch('/api/sign-up', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
}).then((res) => res.json());
}).then((res) => res.json())
router.push("/");
router.push('/')
}}
>
<label htmlFor="email">Email</label>
@@ -253,49 +242,49 @@ export default function SignUpPage() {
<button type="submit">Sign up</button>
</form>
<div>
{"Already have an account? "}
{'Already have an account? '}
<Link href="sign-in">
<a>Sign in</a>
</Link>
</div>
</main>
);
)
}
```
Then add the following code to `/pages/sign-in.tsx`:
```
import Link from "next/link";
import { useRouter } from "next/router";
```ts
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function SignInPage() {
const router = useRouter();
const router = useRouter()
return (
<main>
<form
onSubmit={async (e) => {
e.preventDefault();
e.preventDefault()
const target = e.target as typeof e.target & {
email: { value: string };
password: { value: string };
};
email: { value: string }
password: { value: string }
}
const email = target.email.value;
const password = target.password.value;
const email = target.email.value
const password = target.password.value
const { state, mfaUrl } = await fetch("/api/sign-in", {
method: "POST",
headers: { "Content-Type": "application/json" },
const { state, mfaUrl } = await fetch('/api/sign-in', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
}).then((res) => res.json());
}).then((res) => res.json())
if (state === "CHALLENGE_REQUIRED") {
window.location.href = mfaUrl;
if (state === 'CHALLENGE_REQUIRED') {
window.location.href = mfaUrl
} else {
router.push("/");
router.push('/')
}
}}
>
@@ -312,38 +301,38 @@ export default function SignInPage() {
</Link>
</div>
</main>
);
)
}
```
Now we will use Supabase's `withPageAuth` wrapper around `getServerSideProps` to make the home page require authentication via SSR. Replace the existing code in `/pages/index.tsx` with the following:
```
import { getUser, User, withPageAuth } from "@supabase/auth-helpers-nextjs";
import { GetServerSideProps } from "next";
import { useRouter } from "next/router";
import { authsignal } from "../lib/authsignal";
```ts
import { getUser, User, withPageAuth } from '@supabase/auth-helpers-nextjs'
import { GetServerSideProps } from 'next'
import { useRouter } from 'next/router'
import { authsignal } from '../lib/authsignal'
interface Props {
user: User;
isEnrolled: boolean;
user: User
isEnrolled: boolean
}
export const getServerSideProps: GetServerSideProps<Props> = withPageAuth({
redirectTo: "/sign-in",
redirectTo: '/sign-in',
async getServerSideProps(ctx) {
const { user } = await getUser(ctx);
const { user } = await getUser(ctx)
const { isEnrolled } = await authsignal.getUser({userId: user.id});
const { isEnrolled } = await authsignal.getUser({ userId: user.id })
return {
props: { user, isEnrolled },
};
}
},
});
})
export default function HomePage({ user, isEnrolled }: Props) {
const router = useRouter();
const router = useRouter()
return (
<main>
@@ -351,29 +340,29 @@ export default function HomePage({ user, isEnrolled }: Props) {
<div> Signed in as: {user?.email}</div>
<button
onClick={async (e) => {
e.preventDefault();
e.preventDefault()
const { mfaUrl } = await fetch("/api/mfa", {
method: "POST",
headers: { "Content-Type": "application/json" },
const { mfaUrl } = await fetch('/api/mfa', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ isEnrolled }),
}).then((res) => res.json());
}).then((res) => res.json())
window.location.href = mfaUrl;
window.location.href = mfaUrl
}}
>
{isEnrolled ? "Manage MFA settings" : "Set up MFA"}
{isEnrolled ? 'Manage MFA settings' : 'Set up MFA'}
</button>
<button onClick={() => router.push("/api/sign-out")}>Sign out</button>
<button onClick={() => router.push('/api/sign-out')}>Sign out</button>
</section>
</main>
);
)
}
```
Optional: To make things look a bit nicer, you can add the following to `/styles/globals.css`:
```
```css
main {
min-height: 100vh;
display: flex;
@@ -435,113 +424,95 @@ Now we'll replace the existing api routes in `/pages/api/` with 5 new routes:
Add the following code to `/pages/api/sign-in.ts`:
```
import { supabaseClient } from "@supabase/auth-helpers-nextjs";
import { NextApiRequest, NextApiResponse } from "next";
import { authsignal } from "../../lib/authsignal";
import { setAuthCookie, setTempCookie } from "../../lib/cookies";
```ts
import { supabaseClient } from '@supabase/auth-helpers-nextjs'
import { NextApiRequest, NextApiResponse } from 'next'
import { authsignal } from '../../lib/authsignal'
import { setAuthCookie, setTempCookie } from '../../lib/cookies'
export default async function signIn(
req: NextApiRequest,
res: NextApiResponse
) {
const { email, password } = req.body;
export default async function signIn(req: NextApiRequest, res: NextApiResponse) {
const { email, password } = req.body
const { data, error } = await supabaseClient.auth.api.signInWithEmail(
email,
password
);
const { data, error } = await supabaseClient.auth.api.signInWithEmail(email, password)
if (error || !data?.user) {
return res.send({ error });
return res.send({ error })
}
const { state, url: mfaUrl } = await authsignal.track({
action: "signIn",
action: 'signIn',
userId: data.user.id,
});
})
if (state === "CHALLENGE_REQUIRED") {
await setTempCookie(data, res);
if (state === 'CHALLENGE_REQUIRED') {
await setTempCookie(data, res)
} else {
setAuthCookie(data, res);
setAuthCookie(data, res)
}
res.send({ state, mfaUrl });
res.send({ state, mfaUrl })
}
```
Then to handle new sign-ups add the following to `/pages/api/sign-up.ts`:
```
import { supabaseClient } from "@supabase/auth-helpers-nextjs";
import { Session } from "@supabase/supabase-js";
import { NextApiRequest, NextApiResponse } from "next";
import { setAuthCookie } from "../../lib/cookies";
```ts
import { supabaseClient } from '@supabase/auth-helpers-nextjs'
import { Session } from '@supabase/supabase-js'
import { NextApiRequest, NextApiResponse } from 'next'
import { setAuthCookie } from '../../lib/cookies'
export default async function signUp(
req: NextApiRequest,
res: NextApiResponse
) {
const { email, password } = req.body;
export default async function signUp(req: NextApiRequest, res: NextApiResponse) {
const { email, password } = req.body
const { data, error } = await supabaseClient.auth.api.signUpWithEmail(
email,
password
);
const { data, error } = await supabaseClient.auth.api.signUpWithEmail(email, password)
if (error || !isSession(data)) {
res.send({ error });
res.send({ error })
} else {
setAuthCookie(data, res);
res.send({ data });
setAuthCookie(data, res)
res.send({ data })
}
}
const isSession = (data: any): data is Session => !!data?.access_token;
const isSession = (data: any): data is Session => !!data?.access_token
```
To clear the auth cookies on sign-out add the following to `/pages/api/sign-out.ts`:
```
import { supabaseClient } from "@supabase/auth-helpers-nextjs";
import { NextApiRequest, NextApiResponse } from "next";
```ts
import { supabaseClient } from '@supabase/auth-helpers-nextjs'
import { NextApiRequest, NextApiResponse } from 'next'
export default async function signOut(
req: NextApiRequest,
res: NextApiResponse
) {
supabaseClient.auth.api.deleteAuthCookie(req, res, { redirectTo: "/sign-in" });
export default async function signOut(req: NextApiRequest, res: NextApiResponse) {
supabaseClient.auth.api.deleteAuthCookie(req, res, { redirectTo: '/sign-in' })
}
```
To handle the user's actions to set up MFA or manage their existing MFA settings, add the following to `/pages/api/mfa.ts`:
```
import { getUser, withApiAuth } from "@supabase/auth-helpers-nextjs";
import { NextApiRequest, NextApiResponse } from "next";
import { authsignal } from "../../lib/authsignal";
```ts
import { getUser, withApiAuth } from '@supabase/auth-helpers-nextjs'
import { NextApiRequest, NextApiResponse } from 'next'
import { authsignal } from '../../lib/authsignal'
export default withApiAuth(async function mfa(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== "POST") {
return res.status(405).send({ message: "Only POST requests allowed" });
export default withApiAuth(async function mfa(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') {
return res.status(405).send({ message: 'Only POST requests allowed' })
}
const { user } = await getUser({ req, res });
const { user } = await getUser({ req, res })
const { isEnrolled } = req.body;
const { isEnrolled } = req.body
const { url: mfaUrl } = await authsignal.track({
action: isEnrolled ? "manageSettings" : "enroll",
action: isEnrolled ? 'manageSettings' : 'enroll',
userId: user.id,
redirectToSettings: isEnrolled,
});
})
res.send({ mfaUrl });
});
res.send({ mfaUrl })
})
```
Because the user should be authenticated with Supabase to set up or manage MFA, we can use Supabase's `withApiAuth` wrapper to protect this route.
@@ -550,28 +521,25 @@ The `redirectToSettings` param specifies whether the user should be redirected t
Finally we need a route to handle the redirect back from Authsignal after an MFA challenge. Add the following to `/pages/api/callback.ts`:
```
import { NextApiRequest, NextApiResponse } from "next";
import { authsignal } from "../../lib/authsignal";
import { getSessionFromTempCookie, setAuthCookie } from "../../lib/cookies";
```ts
import { NextApiRequest, NextApiResponse } from 'next'
import { authsignal } from '../../lib/authsignal'
import { getSessionFromTempCookie, setAuthCookie } from '../../lib/cookies'
export default async function callback(
req: NextApiRequest,
res: NextApiResponse
) {
const token = req.query.token as string;
export default async function callback(req: NextApiRequest, res: NextApiResponse) {
const token = req.query.token as string
const { success } = await authsignal.validateChallenge({ token });
const { success } = await authsignal.validateChallenge({ token })
if (success) {
const session = await getSessionFromTempCookie(req);
const session = await getSessionFromTempCookie(req)
if (session) {
setAuthCookie(session, res);
setAuthCookie(session, res)
}
}
res.redirect("/");
res.redirect('/')
}
```
+35 -33
View File
@@ -3,62 +3,64 @@ import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'estuary',
title: 'Estuary',
description: 'Create a real-time data pipeline connecting Firestore and Supabase to make migration simple.',
description:
'Create a real-time data pipeline connecting Firestore and Supabase to make migration simple.',
}
Estuary Flow is a platform for creating [real-time data pipelines at scale](https://www.estuary.dev/).
It combines the intuitive interface of an ELT service with an event-driven runtime and a variety of open-source connectors.
It combines the intuitive interface of an ELT service with an event-driven runtime and a variety of open-source connectors.
You can use Flow to migrate your data from Firestore to the Postgres database in your Supabase project.
You can use Flow to migrate your data from Firestore to the Postgres database in your Supabase project.
You do this by building a real-time pipeline that captures data from Firestore and materializes (loads) that data to Postgres.
Once created, the pipeline backfills all your historical data from Firestore and continues to process new data events in real time.
Once created, the pipeline backfills all your historical data from Firestore and continues to process new data events in real time.
## Prerequisites
Before you begin, you'll need:
* An [Estuary account](go.estuary.dev/sign-up).
- An [Estuary account](https://go.estuary.dev/sign-up).
* For your Firestore database:
* A Google service account with read access to your Firestore database, via [roles/datastore.viewer](https://cloud.google.com/datastore/docs/access/iam). You can assign this role when you [create the service account](https://cloud.google.com/iam/docs/creating-managing-service-accounts#creating), or [add it to an existing service account](https://cloud.google.com/iam/docs/granting-changing-revoking-access#single-role).
* A generated [JSON service account key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys#creating) for the service account.
- For your Firestore database:
* A Supabase project
- A Google service account with read access to your Firestore database, via [roles/datastore.viewer](https://cloud.google.com/datastore/docs/access/iam). You can assign this role when you [create the service account](https://cloud.google.com/iam/docs/creating-managing-service-accounts#creating), or [add it to an existing service account](https://cloud.google.com/iam/docs/granting-changing-revoking-access#single-role).
- A generated [JSON service account key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys#creating) for the service account.
- A Supabase project
## Step 1: Capture your Firestore data
You'll start by creating a **capture**, a task in Flow that connects to your data source system: in this case, Firestore. This process will create one or more data **collections**, backed by a real-time data lake.
You'll start by creating a **capture**, a task in Flow that connects to your data source system: in this case, Firestore. This process will create one or more data **collections**, backed by a real-time data lake.
1. Go to the Flow web application at [dashboard.estuary.dev](http://dashboard.estuary.dev) and sign in using the credentials provided by your Estuary account manager.
2. Click the **Captures** tab and choose **New Capture**.
3. Locate and select the **Google Firestore** card.
3. Locate and select the **Google Firestore** card.
A form appears with the properties required for a Firestore capture.
4. Set a name for your capture.
4. Set a name for your capture.
Click inside the **Name** field to generate a drop-down menu of available **prefixes** and select one (likely, this will be the name of your organization). Append a unique capture name after the `/` to create the full name, for example, `acmeCo/myFirestoreCapture`.
5. Fill out the required properties for Firestore.
* **Database**: Flow can autodetect the database name, but you may optionally specify it here. This is helpful if the service account used has access to multiple Firebase projects. Your database name usually follows the format `projects/$PROJECTID/databases/(default)`.
* **Credentials**: The JSON service account key created per the prerequisites.
- **Database**: Flow can autodetect the database name, but you may optionally specify it here. This is helpful if the service account used has access to multiple Firebase projects. Your database name usually follows the format `projects/$PROJECTID/databases/(default)`.
- **Credentials**: The JSON service account key created per the prerequisites.
6. Click **Discover Endpoint**.
Flow uses the provided configuration to initiate a connection with Firestore. It generates a capture specification and details of the collections that it will create, once published.
Flow uses the provided configuration to initiate a connection with Firestore. It generates a capture specification and details of the collections that it will create, once published.
7. Use the **Specification Editor** to view the [JSON schemas](https://docs.estuary.dev/concepts/schemas/) for each collection and make sure they are formatted correctly for your needs. If they're not, you can edit them.
7. Use the **Specification Editor** to view the [JSON schemas](https://docs.estuary.dev/concepts/schemas/) for each collection and make sure they are formatted correctly for your needs. If they're not, you can edit them.
8. Click **Save and publish**.
You'll see a notification when the capture publishes successfully.
You'll see a notification when the capture publishes successfully.
The data currently in your Firestore database has been captured, and future updates to it will be captured continuously.
The data currently in your Firestore database has been captured, and future updates to it will be captured continuously.
Click **Materialize Collections** to continue.
Click **Materialize Collections** to continue.
## Step 2: Materialize your collections to Postgres
@@ -66,35 +68,35 @@ Next, you'll add a Postgres materialization to connect the captured collections
1. On the **Create Materialization** page, search for and select the **PostgreSQL** tile.
A form appears with the properties required for a Postgres materialization.
A form appears with the properties required for a Postgres materialization.
2. Choose a unique name for your materialization like you did when naming your capture; for example, `acmeCo/mySupabaseMaterialization`.
3. Fill out the required properties for PostgreSQL. You can find most of these in Supabase by going to the **Settings** section and clicking **Database**.
* **Address**: Format at `<host>:<port>`.
* **User**: Usually, this is `postgres`.
* **Password**: The password you set when you created your Supabase project.
- **Address**: Format at `<host>:<port>`.
- **User**: Usually, this is `postgres`.
- **Password**: The password you set when you created your Supabase project.
4. Scroll down to view the **Collection Selector** and fill in the **Table** field for each collection.
The collections you just created have already been selected, but you must provide names for the tables to which they'll be materialized.
The collections you just created have already been selected, but you must provide names for the tables to which they'll be materialized.
5. Click **Discover Endpoint**.
Flow uses the provided configuration to initiate a connection to your Supabase Postgres database and generate a specification.
5. Click **Discover Endpoint**.
6. Click **Save and Publish**. You'll see a notification when the full materialization publishes successfully.
Flow uses the provided configuration to initiate a connection to your Supabase Postgres database and generate a specification.
Your Firestore collections are copied to tables in Supabase. As long as you leave the capture and materialation running, any changes to the Firestore data will be reflected in Supabase in milliseconds.
6. Click **Save and Publish**. You'll see a notification when the full materialization publishes successfully.
Your Firestore collections are copied to tables in Supabase. As long as you leave the capture and materialation running, any changes to the Firestore data will be reflected in Supabase in milliseconds.
## Resources
For more information, visit the [Flow docs](https://docs.estuary.dev/). In particular:
* [Guide to create a Data Flow](https://docs.estuary.dev/guides/create-dataflow/)
* [Firestore capture connector](https://docs.estuary.dev/reference/Connectors/capture-connectors/google-firestore/)
* [Postgres materializaiton connector](https://docs.estuary.dev/reference/Connectors/materialization-connectors/PostgreSQL/)
- [Guide to create a Data Flow](https://docs.estuary.dev/guides/create-dataflow/)
- [Firestore capture connector](https://docs.estuary.dev/reference/Connectors/capture-connectors/google-firestore/)
- [Postgres materializaiton connector](https://docs.estuary.dev/reference/Connectors/materialization-connectors/PostgreSQL/)
export const Page = ({ children }) => <Layout meta={meta} children={children} />
+27 -9
View File
@@ -63,21 +63,38 @@ This project comes with TypeScript configured and has the following structure.
### Configuring the project to use PostgreSQL
Go ahead and delete the `prisma/dev.db` file because we will be switching to PostgreSQL.
Next, inside the `prisma/.env` file, update the value of the `DATABASE_URL` variable to the connection string you got in **step 1**. The URL might look as follows:
By default, Prisma migrations will try to drop the `postgres` database, which can lead to conflicts with Supabase databases. For this scenario, use [Prisma Shadow Databases](https://www.prisma.io/docs/concepts/components/prisma-migrate/shadow-database#cloud-hosted-shadow-databases-must-be-created-manually).
```env
# prisma/.env
postgres://postgres:[YOUR-PASSWORD]@db.vdbnhqozmlzdsaejdxwr.supabase.co:5432/postgres
Create a shadow database in your PostgreSQL server within the same Supabase project using the `psql` CLI and the `DATABASE_URL` from the previous steps (or use the local database).
```bash
psql postgresql://postgres:[YOUR-PASSWORD]@db.vdbnhqozmlzdsaejdxwr.supabase.red:5432
```
Finally, inside your `schema.prisma` file, change the `provider` from “sqlite” to `“postgresql”`.
After you connect to your project's PostgreSQL instance, create another database (e.g., `postgres_shadow`):
```bash
postgres=> CREATE DATABASE postgres_shadow;
postgres=> exit
```
Go ahead and delete the `prisma/dev.db` file because we will be switching to PostgreSQL.
In the `.env` file, update `DATABASE_URL` and `SHADOW_DATABASE_URL` to the connection string from **step 1**. The `.env` file should look like:
```env
# .env
DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.vdbnhqozmlzdsaejdxwr.supabase.co:5432/postgres"
SHADOW_DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.vdbnhqozmlzdsaejdxwr.supabase.co:5432/postgres_shadow"
```
In the `schema.prisma` file, change the `provider` from "sqlite" to `"postgresql"` and add the `shadowDatabaseUrl` property.
This is what your `schema.prisma` file should look like:
```go
datasource db {
provider = “postgresql”
url = env(“DATABASE_URL”)
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}
generator client {
provider = “prisma-client-js”
@@ -124,11 +141,12 @@ This will create a `prisma/migrations` folder inside your `prisma` directory and
If you’re working in a serverless environment (for example Node.js functions hosted on AWS Lambda, Vercel or Netlify Functions), you need to set up [connection pooling](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#serverless-environments-faas) using a tool like [PgBouncer](https://www.pgbouncer.org/). That’s because every function invocation may result in a [new connection to the database](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#the-serverless-challenge). Supabase [supports connection management using PgBouncer](https://supabase.io/blog/2021/04/02/supabase-pgbouncer#what-is-connection-pooling) and are enabled by default.
Go to the **Database** page from the sidebar in the Supabase dashboard and navigate to **connection pool** settings
![Connection pool settings](/docs/img/guides/integrations/prisma/w0oowg8vq435ob5c3gf0.png)
When running migrations you need to use the non pooled connection URL (like the one we used in **step 1**). However, when deploying your app, you’ll use the pooled connection URL and add the `?pgbouncer=true` flag to the PostgreSQL connection URL. To minimize the number of concurrent connections, setting the `connection_limit` to `1` is also recommended. So the URL might look as follows:
When migrating, you need to use the non-pooled connection URL (like the one used in **step 1**). However, when deploying your app, use the pooled connection URL and add the `?pgbouncer=true` flag to the PostgreSQL connection URL. It's also recommended to minimize the number of concurrent connections by setting the `connection_limit` to `1`. The `.env` file should look like:
```env
# prisma/.env
postgres://postgres:[YOUR-PASSWORD]@db.vdbnhqozmlzdsaejdxwr.supabase.co:6543/postgres?pgbouncer=true&connection_limit=1
# .env
DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.vdbnhqozmlzdsaejdxwr.supabase.co:6543/postgres?pgbouncer=true&connection_limit=1"
SHADOW_DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.vdbnhqozmlzdsaejdxwr.supabase.co:5432/postgres_shadow"
```
Prisma Migrate uses database transactions to check out the current state of the database and the migrations table. However, the Migration Engine is designed to use a single connection to the database, and does not support connection pooling with PgBouncer. If you attempt to run Prisma Migrate commands in any environment that uses PgBouncer for connection pooling, you might see the following error:
@@ -82,7 +82,7 @@ You'll use this client to query for data in your synced schema.
## Resources
- [Sequin](https://sequin.io) official website.
- [Sequin Console](https://app.sequin.io*).
- [Sequin Console](https://app.sequin.io).
- [Sequin](https://docs.sequin.io/welcome) documentation.
- [Sequin + Supabase + Stripe Tutorial](https://github.com/sequin-io/build-a-saas-with-next-js-supabase-stripe-and-sequin)
@@ -51,7 +51,7 @@ The password is the same password you used when creating the Supabase project.
![supabase-connection-db-info](/docs/img/guides/integrations/snaplet/supabase-connection-db-info.png)
You’ll have to confirm providing Snaplet access to your database. Snaplet will prompt you to only provide `read-only` access to your database. Snaplet has a guide in their documentation on how to do so [here](https://docs.snaplet.dev/postgresql/create-read-only-role).
You’ll have to confirm providing Snaplet access to your database. Snaplet will prompt you to only provide `read-only` access to your database. Snaplet has a guide in their documentation on how to do so [here](https://docs.snaplet.dev/guides/postgresql/#create-read-only-role).
> Note that whatever connection string you provide here will be that of your Data Source – essentially the production database in a real-life scenario
@@ -39,12 +39,7 @@ Once you have added both to WeWeb, you will have the option to enable realtime t
**🚨 Warning 🚨**
> Realtime is disabled by default in Supabase for better database performance and security. Learn more about [realtime functionalities](https://supabase.com/docs/guides/database#realtime) in our documentation.
> Member
> @awalias awalias 16 days ago
> might just wanna change these ones to be relative but otherwise looks great 👍
@EWMAs Reply...
> Realtime is disabled by default in Supabase for better database performance and security. Learn more about [realtime functionalities](/docs/guides/realtime).
## Step 2: GET Data from Supabase
@@ -108,7 +103,7 @@ If we only ask for the data from the `location` field of the `vehicles` table, S
Assuming you were able to fetch data from Supabase in a WeWeb Collection, you'll be able to bind the data from that Collection on your WeWeb pages.
In the example below, we chose to display the car model and mileage in the [Data Grid element](https://docs.weweb.io/data-grid/) that comes out-of-the-box in WeWeb:
In the example below, we chose to display the car model and mileage in the [Data Grid element](https://docs.weweb.io/add-elements/elements/data-grid.html) that comes out-of-the-box in WeWeb:
![](https://weweb-changelog.ghost.io/content/images/2022/10/CleanShot-2022-10-21-at-12.31.05@2x.png)
@@ -116,7 +111,7 @@ We chose this element because it includes a built-in inline editing mode we'll w
#### 🔥 Pro Tip 🔥
> In WeWeb, you can [bind arrays of data to any Container](https://docs.weweb.io/display-collection-list/). Just bear in mind that the first child of the Container you bind the Collection to will be the repeated item. With that in mind, you might want the first child Element to be another Container with a number of items inside like a title, description, button or image.
> In WeWeb, you can [bind arrays of data to any Container](https://docs.weweb.io/binding-filtering/display-data.html). Just bear in mind that the first child of the Container you bind the Collection to will be the repeated item. With that in mind, you might want the first child Element to be another Container with a number of items inside like a title, description, button or image.
## Step 4: Update a record in Supabase
@@ -143,7 +143,8 @@ As with the final activation stage of the process for setting up a vanity subdom
## Limitations
Edge functions do not honor the custom domain or the vanity subdomain setting and they still have to be invoked via the `foobarbaz.supabase.co` domain.
- Edge functions do not honor the custom domain or the vanity subdomain setting and they still have to be invoked via the `foobarbaz.supabase.co` domain.
- A Supabase project can—at this time—use either a Custom Domain or a Vanity Subdomain, but not both.
export const Page = ({ children }) => <Layout meta={meta} children={children} />
@@ -20,7 +20,7 @@ To get started:
1. [Install](/docs/guides/cli) the Supabase CLI 1.22.0+.
1. [Log in](/docs/guides/cli/local-development#log-in-to-the-supabase-cli) to your Supabase account using the CLI.
1. Ensure that you have [Owner or Admin permissions](/docs/guides/hosting/platform/access-control#manage-team-members) for the project that you are enabling network restrictions.
1. Ensure that you have [Owner or Admin permissions](/docs/guides/platform/access-control#manage-team-members) for the project that you are enabling network restrictions.
## Check restrictions
-10
View File
@@ -76,16 +76,6 @@ export const meta = {
</div>
<div>
### Self-Hosting Supabase
We're happy to host your next big thing, but we also make it easy to host Supabase on your own. We use Docker to help make setup as painless as possible.
[Learn more](/docs/guides/resources/self-hosting)
</div>
</div>
{/* end of container */}
@@ -54,7 +54,7 @@ git init
supabase init
```
Make sure Docker is running. The [start](/docs/reference/cli/usage#supabase-start) command uses Docker to start the Supabase [services](/docs/architecture).
Make sure Docker is running. The [start](/docs/reference/cli/usage#supabase-start) command uses Docker to start the Supabase [services](/docs/guides/getting-started/architecture).
This command may take a while to run if this is the first time using the CLI.
```bash
@@ -1,16 +1,14 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'overview',
title: 'Self Hosting',
sidebar_label: 'Overview',
description: 'Getting started with Self Hosting.',
title: 'Self-Hosting',
description: 'Getting started with self-hosting Supabase.',
}
There are several ways to use Supabase:
- [Supabase Cloud](https://app.supabase.com): you don't need to deploy anything. We will manage and scale your infrastructure.
- [Docker](../../guides/hosting/docker): deploy to your own infrastructure.
- [Docker](/docs/guides/self-hosting/docker): deploy to your own infrastructure.
- Kubernetes: coming soon.
## Architecture
@@ -74,7 +72,7 @@ For working with JWT and Auth functions.
Supabase creates several default roles in your Postgres database. To restore defaults at any time you can run the commands inside the
[schema initialization scripts](https://github.com/supabase/postgres/tree/develop/migrations/db/init-scripts). Remember to change your
[role passwords](./docker#securing-your-setup) before deploying to production environments.
[role passwords](/docs/guides/self-hosting/docker#securing-your-setup) before deploying to production environments.
##### `postgres`
@@ -151,7 +149,7 @@ We realize that database migrations are difficult, and this is one of the proble
While Supabase officially supports Docker, we have several other deployment strategies managed by the community:
- [supabase-docker](../../guides/hosting/docker) (Official)
- [supabase-docker](/docs/guides/self-hosting/docker) (Official)
- [supabase-kubernetes](https://github.com/supabase-community/supabase-kubernetes) (Unofficial)
- [supabase-terraform](https://github.com/supabase-community/supabase-terraform) (Unofficial)
- [supabase-traefik](https://github.com/supabase-community/supabase-traefik) (Unofficial)
@@ -0,0 +1,38 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { getAuthConfigV1 } from '~/lib/mdx/getConfig'
import Param from '~/components/Params'
export const spec = getAuthConfigV1()
export const meta = {
title: 'Auth Self-hosting Config',
description: 'How to configure and deploy Supabase.',
}
<div>{spec.info.description}</div>
<div>
{spec.info.tags.map(tag => {
return (
<>
<h2 className="text-scale-1200">{tag.title}</h2>
<p className="text-scale-1000">{tag.description}</p>
<div className="not-prose">
<h5 className="text-base text-scale-1200 mb-3">Parameters</h5>
<ul>
{spec.parameters.filter(param => param.tags.includes(tag.id)).map((param) => {
return <Param name={param.title} type={param.type} description={param.description} required={param.required} />
})}
</ul>
</div>
</>
)
})}
</div>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'docker',
title: 'Self-hosting with Docker',
description: 'How to configure and deploy Supabase.',
sidebar_label: 'Docker',
title: 'Self-Hosting with Docker',
description: 'Learn how to configure and deploy Supabase with Docker.',
}
Docker is the easiest way to get started with self-hosted Supabase.
@@ -13,7 +11,7 @@ Docker is the easiest way to get started with self-hosted Supabase.
You need the following installed in your system:
- [Docker](https://docs.docker.com/engine/install/) and [docker-compose](https://docs.docker.com/compose/install/)
- [Docker](https://docs.docker.com/engine/install/) and [docker compose](https://docs.docker.com/compose/install/)
- [Git](https://git-scm.com/downloads)
## Quick Start
@@ -33,7 +31,7 @@ cd supabase/docker
cp .env.example .env
# Start
docker-compose up
docker compose up
```
Now visit [http://localhost:3000](http://localhost:3000) to start using Supabase Studio.
@@ -42,11 +40,11 @@ Now visit [http://localhost:3000](http://localhost:3000) to start using Supabase
While we provided you with some example secrets for getting started, you should NEVER deploy your Supabase setup using the defaults we have provided.
Please follow these steps to secure your Docker setup. We [strongly recommend](../../guides/hosting/overview#managing-your-secrets) using a secrets manager when deploying to production.
Follow these steps to secure your Docker setup. We strongly recommend using a [secrets manager](../self-hosting#managing-your-secrets) when deploying to production.
### Generate API Keys
Use your `JWT_SECRET` to generate a `anon` and `service` API keys using the [JWT generator](../../guides/hosting/overview#api-keys).
Use your `JWT_SECRET` to generate a `anon` and `service` API keys using the [JWT generator](../self-hosting#api-keys).
Replace the values in these files:
@@ -72,7 +70,7 @@ The Docker setup doesn't include a management database for managing users and lo
## Configuration
Each system can be [configured](../../guides/hosting/overview#configuration) to suit your particular use-case.
Each system can be [configured](../self-hosting#configuration) to suit your particular use-case.
To keep the setup simple, we made some choices that may not be optimal for production:
@@ -82,7 +80,7 @@ To keep the setup simple, we made some choices that may not be optimal for produ
### Using an external database
We strongly [recommend](../../guides/hosting/overview#managing-your-database) that you decouple your database from `docker-compose` before deploying.
We strongly recommend [decoupling your database](../self-hosting#managing-your-database) from `docker-compose` before deploying.
The middleware will run with any PostgreSQL database that has logical replication enabled. The following environment variables should be updated
in the `.env` file to point to your external database:
@@ -99,7 +97,7 @@ Once you have done this, you can safely comment out the `db` section of the `doc
### Setting database's `log_min_messages`
By default, `docker-compose` sets the database's `log_min_messages` configuration to `fatal` to prevent redundant logs generated by Realtime.
By default, `docker compose` sets the database's `log_min_messages` configuration to `fatal` to prevent redundant logs generated by Realtime.
However, you might miss important log messages such as database errors. Configure `log_min_messages` based on your needs.
### File storage backend on macOS
@@ -0,0 +1,38 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { getRealtimeConfigV0 } from '~/lib/mdx/getConfig'
import Param from '~/components/Params'
export const spec = getRealtimeConfigV0()
export const meta = {
title: 'Realtime Self-hosting Config',
description: 'How to configure and deploy Supabase.',
}
<div>{spec.info.description}</div>
<div>
{spec.info.tags.map(tag => {
return (
<>
<h2 className="text-scale-1200">{tag.title}</h2>
<p className="text-scale-1000">{tag.description}</p>
<div className="not-prose">
<h5 className="text-base text-scale-1200 mb-3">Parameters</h5>
<ul>
{spec.parameters.filter(param => param.tags.includes(tag.id)).map((param) => {
return <Param name={param.title} type={param.type} description={param.description} required={param.required} />
})}
</ul>
</div>
</>
)
})}
</div>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
@@ -0,0 +1,38 @@
import Layout from '~/layouts/DefaultGuideLayout'
import { getStorageConfigV0 } from '~/lib/mdx/getConfig'
import Param from '~/components/Params'
export const spec = getStorageConfigV0()
export const meta = {
title: 'Storage Self-hosting Config',
description: 'How to configure and deploy Supabase.',
}
<div>{spec.info.description}</div>
<div>
{spec.info.tags.map(tag => {
return (
<>
<h2 className="text-scale-1200">{tag.title}</h2>
<p className="text-scale-1000">{tag.description}</p>
<div className="not-prose">
<h5 className="text-base text-scale-1200 mb-3">Parameters</h5>
<ul>
{spec.parameters.filter(param => param.tags.includes(tag.id)).map((param) => {
return <Param name={param.title} type={param.type} description={param.description} required={param.required} />
})}
</ul>
</div>
</>
)
})}
</div>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
export default Page
+16 -5
View File
@@ -11,9 +11,9 @@ 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. [Learn more](/docs/architecture) about Supabase, start with the
[tutorials](/docs/guides/getting-started#tutorials), or dive straight into the different products
and APIs.
build a product. [Learn more](/docs/guides/getting-started/architecture) about Supabase, start
with the [tutorials](/docs/guides/getting-started#tutorials), or dive straight into the different
products and APIs.
</h2>
{/* start container */}
@@ -30,7 +30,7 @@ export const meta = {
">
<div className="col-span-12 lg:col-span-7 flex flex-col gap-6">
<img className="hidden lg:visible absolute right-0 top-0 w-[500px]" src="/docs/img/cards/dark.png" />
<div classNam="">
<div className="">
<h3 className="text-2xl m-0 mb-3">Getting Started</h3>
Discover how to set up a database to an app making queries in just a few minutes.
@@ -144,7 +144,7 @@ export const meta = {
key={product.title}
passHref
>
<a className={'col-span-12 md:col-span-4'}>
<a className={'col-span-12 md:col-span-6'}>
<GlassPanel {...product} background={false} showIconBg={true}>
{product.description}
</GlassPanel>
@@ -243,6 +243,7 @@ export const apis = [
hasLightIcon: true,
href: 'reference/javascript/introduction',
description: 'Official client libraries for JavaScript and TypeScript.',
span: 'col-span-12 md:col-span-6',
},
{
title: 'Flutter Client Library',
@@ -250,6 +251,7 @@ export const apis = [
hasLightIcon: true,
href: 'reference/dart/introduction',
description: 'Official client libraries for Flutter and Dart.',
span: 'col-span-12 md:col-span-6',
},
{
title: 'Management API',
@@ -257,6 +259,7 @@ export const apis = [
hasLightIcon: true,
href: 'reference/api/introduction',
description: 'Manage your Supabase projects and organizations programmatically.',
span: 'col-span-12 md:col-span-6',
},
{
title: 'Supabase CLI',
@@ -265,6 +268,7 @@ export const apis = [
href: 'reference/cli/introduction',
description:
'Use the CLI to develop your project locally, manage database migrations, and deploy to the Supabase Platform or self-host.',
span: 'col-span-12 md:col-span-6',
},
{
title: 'Self-Hosting Auth',
@@ -307,6 +311,13 @@ export const platform = [
href: 'guides/resources',
description: 'View examples, curated content from the community, migration guides, and more.',
},
{
title: 'Self-Hosting Supabase',
icon: '/docs/img/icons/menu/platform',
hasLightIcon: true,
href: 'guides/self-hosting',
description: 'Learn how to self-host Supabase and deploy to your own infrastructure.',
},
{
// header: '/docs/img/cards/sample-card-header-2.svg',
title: 'Integrations',
@@ -7,6 +7,7 @@ import { flattenSections } from '~/lib/helpers'
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
import { useRouter } from 'next/router'
import RefSEO from '~/components/reference/RefSEO'
const sections = flattenSections(clientLibsCommonSections)
@@ -15,13 +16,21 @@ export default function JSReference(props) {
const slug = router.query.slug[0]
const filteredSection = sections.filter((section) => section.id === slug)
const pageTitle = filteredSection[0]?.title
? `${filteredSection[0]?.title} | Supabase`
: 'Supabase'
return (
<RefSectionHandler
sections={filteredSection}
spec={spec}
typeSpec={typeSpec}
pageProps={props}
/>
<>
<RefSEO title={pageTitle} />
<RefSectionHandler
sections={filteredSection}
spec={spec}
typeSpec={typeSpec}
pageProps={props}
/>
</>
)
}
@@ -7,6 +7,7 @@ import { flattenSections } from '~/lib/helpers'
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
import { useRouter } from 'next/router'
import RefSEO from '~/components/reference/RefSEO'
const sections = flattenSections(clientLibsCommonSections)
@@ -15,13 +16,20 @@ export default function JSReference(props) {
const slug = router.query.slug[0]
const filteredSection = sections.filter((section) => section.id === slug)
const pageTitle = filteredSection[0]?.title
? `${filteredSection[0]?.title} | Supabase`
: 'Supabase'
return (
<RefSectionHandler
sections={filteredSection}
spec={spec}
typeSpec={typeSpec}
pageProps={props}
/>
<>
<RefSEO title={pageTitle} />
<RefSectionHandler
sections={filteredSection}
spec={spec}
typeSpec={typeSpec}
pageProps={props}
/>
</>
)
}
@@ -7,6 +7,7 @@ import { flattenSections } from '~/lib/helpers'
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
import { useRouter } from 'next/router'
import RefSEO from '~/components/reference/RefSEO'
const sections = flattenSections(clientLibsCommonSections)
@@ -15,13 +16,21 @@ export default function JSReference(props) {
const slug = router.query.slug[0]
const filteredSection = sections.filter((section) => section.id === slug)
const pageTitle = filteredSection[0]?.title
? `${filteredSection[0]?.title} | Supabase`
: 'Supabase'
return (
<RefSectionHandler
sections={filteredSection}
spec={spec}
typeSpec={typeSpec}
pageProps={props}
/>
<>
<RefSEO title={pageTitle} />
<RefSectionHandler
sections={filteredSection}
spec={spec}
typeSpec={typeSpec}
pageProps={props}
/>
</>
)
}
@@ -7,7 +7,7 @@ import { flattenSections } from '~/lib/helpers'
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
import { useRouter } from 'next/router'
import RefSEO from '~/components/reference/RefSEO'
const sections = flattenSections(clientLibsCommonSections)
export default function JSReference(props) {
@@ -15,13 +15,21 @@ export default function JSReference(props) {
const slug = router.query.slug[0]
const filteredSection = sections.filter((section) => section.id === slug)
const pageTitle = filteredSection[0]?.title
? `${filteredSection[0]?.title} | Supabase`
: 'Supabase'
return (
<RefSectionHandler
sections={filteredSection}
spec={spec}
typeSpec={typeSpec}
pageProps={props}
/>
<>
<RefSEO title={pageTitle} />
<RefSectionHandler
sections={filteredSection}
spec={spec}
typeSpec={typeSpec}
pageProps={props}
/>
</>
)
}
+2 -1
View File
@@ -34,6 +34,7 @@ Kevin Grüneberg
Lakshmipathi G
Laura C
Leo T
Long Hoang
Margarita Sandomirskaia
Marijana Šimag
Mark Burggraf
@@ -60,7 +61,7 @@ ____________
JOBS
If you are interested in working with us check out the open roles here: https://about.supabase.com/careers
If you are interested in working with us check out the open roles here: https://supabase.com/careers
___________
+9 -10
View File
@@ -1,10 +1,9 @@
<svg width="71" height="55" viewBox="0 0 71 55" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z" fill="#5865F2"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="71" height="55" fill="white"/>
</clipPath>
</defs>
</svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="75px" height="59px" viewBox="0 0 75 59" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>discord-icon</title>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="discord-icon" transform="translate(2.000000, 2.411745)" fill-rule="nonzero" stroke="#636262" stroke-width="3">
<path d="M60.1044999,4.48605546 C55.5791999,2.40965546 50.7264999,0.879855461 45.6526999,0.00367546142 C45.5602999,-0.0132345386 45.4679999,0.0290244614 45.4203999,0.113544461 C44.7962999,1.22355546 44.1049999,2.67165546 43.6208999,3.80985546 C38.1636999,2.99285546 32.7344999,2.99285546 27.3891999,3.80985546 C26.9049999,2.64635546 26.1885999,1.22355546 25.5616999,0.113544461 C25.5140999,0.0318444614 25.4217999,-0.0104145386 25.3293999,0.00367546142 C20.2583999,0.877055461 15.4056999,2.40685546 10.8775999,4.48605546 C10.8383999,4.50295546 10.8047999,4.53115546 10.7824999,4.56775546 C1.57794989,18.3191555 -0.94356111,31.7325555 0.29340789,44.9796555 C0.29900489,45.0444555 0.33538589,45.1064555 0.38576089,45.1458555 C6.45865989,49.6056555 12.3412999,52.3131555 18.1146999,54.1077555 C18.2070999,54.1359555 18.3049999,54.1021555 18.3637999,54.0260555 C19.7294999,52.1610555 20.9468999,50.1945555 21.9906999,48.1265555 C22.0522999,48.0054555 21.9934999,47.8617555 21.8675999,47.8138555 C19.9365999,47.0813555 18.0978999,46.1882555 16.3291999,45.1740555 C16.1892999,45.0923555 16.1780999,44.8922555 16.3067999,44.7964555 C16.6789999,44.5175555 17.0512999,44.2273555 17.4066999,43.9343555 C17.4709999,43.8808555 17.5605999,43.8695555 17.6361999,43.9033555 C29.2557999,49.2084555 41.8353999,49.2084555 53.3178999,43.9033555 C53.3934999,43.8667555 53.4830999,43.8780555 53.5501999,43.9315555 C53.9056999,44.2245555 54.2778999,44.5175555 54.6528999,44.7964555 C54.7815999,44.8922555 54.7731999,45.0923555 54.6332999,45.1740555 C52.8645999,46.2079555 51.0258999,47.0813555 49.0920999,47.8110555 C48.9661999,47.8589555 48.9101999,48.0054555 48.9717999,48.1265555 C50.0379999,50.1916555 51.2553999,52.1581555 52.5958999,54.0232555 C52.6518999,54.1021555 52.7525999,54.1359555 52.8449999,54.1077555 C58.6463999,52.3131555 64.5289999,49.6056555 70.6018999,45.1458555 C70.6550999,45.1064555 70.6886999,45.0472555 70.6942999,44.9824555 C72.1746999,29.6673555 68.2146999,16.3639555 60.1967999,4.57055546 C60.1771999,4.53115546 60.1436999,4.50295546 60.1044999,4.48605546 Z M23.7258999,36.9135555 C20.2275999,36.9135555 17.3450999,33.7018555 17.3450999,29.7575555 C17.3450999,25.8132555 20.1716999,22.6015555 23.7258999,22.6015555 C27.3079999,22.6015555 30.1625999,25.8414555 30.1065999,29.7575555 C30.1065999,33.7018555 27.2799999,36.9135555 23.7258999,36.9135555 Z M47.3177999,36.9135555 C43.8195999,36.9135555 40.9370999,33.7018555 40.9370999,29.7575555 C40.9370999,25.8132555 43.7635999,22.6015555 47.3177999,22.6015555 C50.8999999,22.6015555 53.7544999,25.8414555 53.6985999,29.7575555 C53.6985999,33.7018555 50.8999999,36.9135555 47.3177999,36.9135555 Z" id="Shape"></path>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

+156 -156
View File
@@ -24,18 +24,6 @@
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/handbook/contributing</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/handbook/introduction</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/api</loc>
<changefreq>weekly</changefreq>
@@ -103,13 +91,13 @@
</url>
<url>
<loc>https://supabase.com/docs/tutorials/nextjs</loc>
<loc>https://supabase.com/docs/handbook/contributing</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/reference/index</loc>
<loc>https://supabase.com/docs/handbook/introduction</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
@@ -120,84 +108,24 @@
<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/tutorials/nextjs</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/api/generating-types</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-captcha</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-email-templates</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-email</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-helpers</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-magic-link</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-mfa</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/managing-user-data</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/overview</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/phone-login</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/row-level-security</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/server-side-rendering</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/social-login</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/database/arrays</loc>
<changefreq>weekly</changefreq>
@@ -288,6 +216,78 @@
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-captcha</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-email-templates</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-email</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-helpers</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-magic-link</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/auth-mfa</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/managing-user-data</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/overview</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/phone-login</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/row-level-security</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/server-side-rendering</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/social-login</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/functions/auth</loc>
<changefreq>weekly</changefreq>
@@ -643,19 +643,37 @@
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/phone-login/messagebird</loc>
<loc>https://supabase.com/docs/guides/database/extensions/http</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/phone-login/twilio</loc>
<loc>https://supabase.com/docs/guides/database/extensions/pgcron</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/auth/phone-login/vonage</loc>
<loc>https://supabase.com/docs/guides/database/extensions/pgnet</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/database/extensions/pgtap</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/database/extensions/plv8</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/database/extensions/uuid-ossp</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
@@ -691,43 +709,19 @@
</url>
<url>
<loc>https://supabase.com/docs/guides/functions/examples/og-image</loc>
<loc>https://supabase.com/docs/guides/auth/phone-login/messagebird</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/database/extensions/http</loc>
<loc>https://supabase.com/docs/guides/auth/phone-login/twilio</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/database/extensions/pgcron</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/database/extensions/pgnet</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/database/extensions/pgtap</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/database/extensions/plv8</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/database/extensions/uuid-ossp</loc>
<loc>https://supabase.com/docs/guides/auth/phone-login/vonage</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
@@ -835,43 +829,25 @@
</url>
<url>
<loc>https://supabase.com/docs/guides/platform/sso/azure</loc>
<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/platform/sso/gsuite</loc>
<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/platform/sso/okta</loc>
<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/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-storage</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/firestore-data</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/heroku</loc>
<loc>https://supabase.com/docs/guides/getting-started/quickstarts/reactjs</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
@@ -960,30 +936,54 @@
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/platform/sso/azure</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/platform/sso/gsuite</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/platform/sso/okta</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-storage</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/firestore-data</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/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/resources/supabase-cli/local-development</loc>
<changefreq>weekly</changefreq>
+19
View File
@@ -253,3 +253,22 @@ div[role='tablist'] {
max-width: 620px;
}
}
/*
* sets the image in @Next/Image components
* to respect the height of the content
*
*/
.next-image--dynamic-fill {
width: 100%;
grid-column: 1 / -1;
}
.next-image--dynamic-fill > span {
position: relative !important;
}
.next-image--dynamic-fill img {
object-fit: contain;
width: 100% !important;
position: relative !important;
height: unset !important;
}
@@ -37,13 +37,13 @@ GitHub stars rocketed for our two main repos, [supabase](https://github.com/supa
Here are the things that survived well.
### Middleware: docker-compose up
### Middleware: docker compose up
This was the most surprising survivor. Our middleware was served from a single Ubuntu server with 4 CPUs and 8GB of RAM. This server was running our middleware using `docker-compose up`:
This was the most surprising survivor. Our middleware was served from a single Ubuntu server with 4 CPUs and 8GB of RAM. This server was running our middleware using `docker compose up`:
![This image shows in middleware architecture. We used docker-compose to pull up 5 open source tools: Kong, Realtime, PostgREST, PG-API, and PG-BOSS.](/images/blog/supabase-middleware-docker.png)
![This image shows in middleware architecture. We used docker compose to pull up 5 open source tools: Kong, Realtime, PostgREST, PG-API, and PG-BOSS.](/images/blog/supabase-middleware-docker.png)
In case you're wondering why any sane company would use that in production, it's because we weren't planning to launch - the HackerNews post was created by an early GitHub follower, while we were alpha testing, and it was too scary to migrate the middleware while it was servicing the thundering herd. All Supabase projects use the same middleware stack (sans docker-compose), so I guess this counts as as a successful load test.
In case you're wondering why any sane company would use that in production, it's because we weren't planning to launch - the HackerNews post was created by an early GitHub follower, while we were alpha testing, and it was too scary to migrate the middleware while it was servicing the thundering herd. All Supabase projects use the same middleware stack (without docker compose), so I guess this counts as a successful load test.
We have since migrated our middleware to multiple ECS clusters, globally load-balanced using AWS's [Global Accelerator](https://aws.amazon.com/global-accelerator/).
+1 -1
View File
@@ -217,7 +217,7 @@ Also check `kong.yml` inside the `kong` directory where you'll see how all the s
Once you're all set, you can start the stack by running:
```bash
docker-compose up
docker compose up
```
Head over to the [Self Hosting Docs](/docs/guides/self-hosting) for a more complete walk through, it also includes several [one-click deploys](/docs/guides/self-hosting#one-click-deploys), so you can easily deploy into your own cloud hosting provider.
@@ -49,7 +49,7 @@ So today we're shipping Light Mode. Access it in the settings of your [Dashboard
With the help of the community, we [started internationalizing](https://github.com/supabase/supabase/issues/1341) our main repository:
- [Arabic | العربية](https://github.com/supabase/supabase/blob/master/i18n/README.ar.md)
- [Chinese / 中文](https://github.com/supabase/supabase/blob/master/i18n/README.cn.md)
- [Chinese / 中文](https://github.com/supabase/supabase/blob/master/i18n/README.zh-cn.md)
- [Dutch / Nederlands](https://github.com/supabase/supabase/blob/master/i18n/README.nl.md)
- [English](https://github.com/supabase/supabase)
- [French / Français](https://github.com/supabase/supabase/blob/master/i18n/README.fr.md)
@@ -22,7 +22,7 @@ So let's get cracking, we've got a lot to cover. 🚀
## Open Source Spotlight: Kong API Gateway
Supabase provides a whole bunch of features that are made up of a [collection of open-source tools](/docs/architecture). To orchestrate these different services and functionalities we use the [Kong API Gateway](https://github.com/Kong/kong).
Supabase provides a whole bunch of features that are made up of a [collection of open-source tools](/docs/guides/getting-started/architecture). To orchestrate these different services and functionalities we use the [Kong API Gateway](https://github.com/Kong/kong).
As a user of our hosted offering you don't really need to know all this, but we love showcasing the amazing open-source tools that we work with, and so we've invited [Vik Gamov](https://twitter.com/gAmUssA) to give you a little intro to [Kong](https://github.com/Kong/kong):
@@ -76,4 +76,4 @@ Tech stack:
Sign up for the waitlist at [multiplayer.dev](https://multiplayer.dev).
You can follow development in our [open source repository on GitHub](https://github.com/supabase/realtime/tree/multiplayer).
You can follow development in our [open source repository on GitHub](https://github.com/supabase/realtime/tree/develop).
@@ -19,7 +19,7 @@ To keep track of all the great tools and people that “Work With Supabase”, w
![partner-gallery-screenshot.png](/images/blog/partner-gallery/partner-gallery-screenshot.png)
As with anything at Supabase, the Partner Gallery is built on top of the [Supabase stack](/docs/architecture) and is, of course, open-source. If you’re looking to build a partner gallery yourself, feel free to use our template as a start: [github.com/supabase-community/partner-gallery-example](https://github.com/supabase-community/partner-gallery-example). It’s packed with goodies like [Postgres Full Text Search](/docs/guides/database/full-text-search), `next-image` with Supabase Storage, and sending emails based on an insert trigger with Supabase Edge Functions.
As with anything at Supabase, the Partner Gallery is built on top of the [Supabase stack](/docs/guides/getting-started/architecture) and is, of course, open-source. If you’re looking to build a partner gallery yourself, feel free to use our template as a start: [github.com/supabase-community/partner-gallery-example](https://github.com/supabase-community/partner-gallery-example). It’s packed with goodies like [Postgres Full Text Search](/docs/guides/database/full-text-search), `next-image` with Supabase Storage, and sending emails based on an insert trigger with Supabase Edge Functions.
# Partner showcase - new integrations and expert services
@@ -48,7 +48,7 @@ The new hCpatcha integration allows you to implement captcha functionality for a
![supabase-updated-settings-ui-dashboard](/images/blog/2022-july/auth-config.jpeg)
We've started updating our settings UI, starting with the Auth settings. You'll now see updated UI for setting up your Auth providers, Email templates and SMTP config. [Auth settings](https://app.supabase.com/project/_/auth/settings)
We've started updating our settings UI, starting with the Auth settings. You'll now see updated UI for setting up your [Auth providers](https://app.supabase.com/project/_/auth/providers), [Email templates](https://app.supabase.com/project/_/auth/templates), and [SMTP config](https://app.supabase.com/project/_/auth/url-configuration).
## Quick product announcements
@@ -13,7 +13,7 @@ date: '2022-08-09'
toc_depth: 3
---
Supabase is a platform-as-a-service built on top of PostgreSQL and [many other amazing open-source tools](https://supabase.com/docs/architecture). It’s a fantastic option to create data-intensive apps and tooling. Taking advantage of the community-made [Python SDK](https://github.com/supabase-community/supabase-py) and [Slack's Python SDK](https://slack.dev/python-slack-sdk/), you can automate tasks and build apps for several use cases.
Supabase is a platform-as-a-service built on top of PostgreSQL and [many other amazing open-source tools](https://supabase.com/docs/guides/getting-started/architecture). It’s a fantastic option to create data-intensive apps and tooling. Taking advantage of the community-made [Python SDK](https://github.com/supabase-community/supabase-py) and [Slack's Python SDK](https://slack.dev/python-slack-sdk/), you can automate tasks and build apps for several use cases.
## Prerequisites
@@ -161,9 +161,9 @@ Slack links have the following format:
https://<span className="text-scale-1200">ORGANIZATION</span>.slack.com/archives/
<span className="text-scale-1200">channel_id</span>/p<span className="text-scale-1200">message_id</span>
</span>
<br/>
<br />
<span style={{ color: 'maroon' }}>Organization: subdomain used in Slack</span>
<br/>
<br />
<span style={{ color: 'DarkGreen' }}>Channel ID</span>: It is the string that you'll need to enter
in slack channels as the channel id e.g C0000ABC02DE
@@ -418,7 +418,3 @@ If you have any questions please reach out via [Twitter](https://twitter.com/sup
- [Python data loading with Supabase](https://supabase.com/blog/loading-data-supabase-python)
- [Visualizing Supabase Data using Metabase](https://supabase.com/blog/visualizing-supabase-data-using-metabase)
- [Supabase-py (Database) on Replit](https://replit.com/@Supabase/Supabase-py-Database?v=1)
@@ -113,3 +113,16 @@ Don't forget to checkout the new feature's being announced as part of [Launch We
- Any intellectual property developed during the hackathon will belong to the team that developed it. We expect that each team will have an agreement between themselves regarding the IP, but this is not required
- By making a submission you grant Supabase permission to use screenshots, code-snippets and/or links to your project or content of your README on our Twitter, blog, website, email updates, and in the Supabase discord server. Supabase does not make any claims over your IP.
- $1500 prize will be paid by making a GitHub sponsorship to the winning repo, $500/month for 3 months. The goal here is to create a sustainable project that will be continue to be maintained for the duration of the sponsorship period (and hopefully beyond :) )
## More Launch Week 6
- [Day 1: New Supabase Docs, built with Next.js](https://supabase.com/blog/new-supabase-docs-built-with-nextjs)
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Community Day](https://supabase.com/blog/launch-week-6-community-day)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
@@ -145,3 +145,15 @@ We're likely to discover more (no doubt from a few friendly HN comments).
## Next steps
If you want to help with `pg_crdt` the best way is to get involved in the GitHub repo. We have enabled [Discussions](https://github.com/supabase/pg_crdt/discussions) for any and all ideas. If you have experience with CRDTs and you like this approach, don't hesitate to contact one of the team.
## More Launch Week 6
- [Day 1: New Supabase Docs, built with Next.js](https://supabase.com/blog/new-supabase-docs-built-with-nextjs)
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Community Day](https://supabase.com/blog/launch-week-6-community-day)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
@@ -94,6 +94,12 @@ Docs are more than just the tech behind them. The goal of docs is to give you, t
## More Launch Week 6
- [Launch Week 6 Hackathon](https://supabase.com/blog/launch-week-6-hackathon)
- [Who We Hire at Supabase](https://supabase.com/blog/who-we-hire)
- [pg_crdt - an experimental CRDT extension for Postgres](https://supabase.com/blog/postgres-crdt)
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Community Day](https://supabase.com/blog/launch-week-6-community-day)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
@@ -205,3 +205,14 @@ A few features to look out for in the future:
- 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.
## More Launch Week 6
- [Day 1: New Supabase Docs, built with Next.js](https://supabase.com/blog/new-supabase-docs-built-with-nextjs)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Community Day](https://supabase.com/blog/launch-week-6-community-day)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
@@ -128,10 +128,14 @@ For starters, we are looking to support WebAuthn and FIDO2 compliant devices suc
We are grateful to our early MFA users for the support and feedback provided throughout this period. In particular, we would like to thank [Fabian Beer](https://madebyfabian.com), [Cogram](https://www.cogram.com), and [Happl](https://happl.com) whose detailed feedback helped to shape our implementation. We would also like to specially thank the community behind the [pquerna/otp](https://github.com/pquerna/otp) and [ajstarks/svgo](https://github.com/ajstarks/svgo) libraries - their work is indispensable to this implementation.
## More Launch Week 6
- [Day 1: New Supabase Docs, built with Next.js](https://supabase.com/blog/new-supabase-docs-built-with-nextjs)
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Launch Week 6 Hackathon](https://supabase.com/blog/launch-week-6-hackathon)
- [Who We Hire at Supabase](https://supabase.com/blog/who-we-hire)
- [pg_crdt - an experimental CRDT extension for Postgres](https://supabase.com/blog/postgres-crdt)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Community Day](https://supabase.com/blog/launch-week-6-community-day)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
@@ -259,6 +259,8 @@ If you're interested in getting involved or building your own Wrapper, don't hes
- [Day 1: New Supabase Docs, built with Next.js](https://supabase.com/blog/new-supabase-docs-built-with-nextjs)
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Launch Week 6 Hackathon](https://supabase.com/blog/launch-week-6-hackathon)
- [Who We Hire at Supabase](https://supabase.com/blog/who-we-hire)
- [pg_crdt - an experimental CRDT extension for Postgres](https://supabase.com/blog/postgres-crdt)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Community Day](https://supabase.com/blog/launch-week-6-community-day)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
@@ -62,6 +62,8 @@ We're working hard to fix these limitations, so stay tuned for future updates.
- [Day 1: New Supabase Docs, built with Next.js](https://supabase.com/blog/new-supabase-docs-built-with-nextjs)
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Launch Week 6 Hackathon](https://supabase.com/blog/launch-week-6-hackathon)
- [Who We Hire at Supabase](https://supabase.com/blog/who-we-hire)
- [pg_crdt - an experimental CRDT extension for Postgres](https://supabase.com/blog/postgres-crdt)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
@@ -36,7 +36,7 @@ An integration between FlutterFlow and Supabase has been long awaited by the com
This is just the beginning of our integration with FlutterFlow. We're working closely with the FlutterFlow team to deliver more features!
Watch John from the FlutterFlow team demonstrate how you can [get started with FlutterFlow and Supabase](https://youtu.be/TmViZsWEBY0).
Watch John from the FlutterFlow team demonstrate how you can [get started with FlutterFlow and Supabase](https://youtu.be/hw9Q-NjASbU).
### OneSignal
@@ -237,4 +237,7 @@ We plan to continue our open and collaborative trend. There are many companies w
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Who We Hire at Supabase](https://supabase.com/blog/who-we-hire)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
@@ -20,7 +20,7 @@ That's a wrap on Supabase Launch Week Day 6. Here's everything we shipped in one
For a developer tool, documentation is more than a resource - it's part of the product. For the past two years at Supabase, this part of our product hasn't been great. Our new docs, built with Next.js, feature a completely new design, better navigation, and the promise of a fully-integrated experience.
- Read the [announcememt](/blog/new-supabase-docs-built-with-nextjs)
- Read the [announcement](/blog/new-supabase-docs-built-with-nextjs)
- Watch the [recap on YouTube](https://www.youtube.com/watch?v=Q1Amk6iDlF8)
- Check out [the new docs](https://supabase.com/docs)
@@ -68,3 +68,15 @@ Before choosing to upgrade, don't forget to check the list of rare cases that ne
- If you're self-hosting, upgrading between major PostgreSQL is easily done with [pg_upgrade](https://www.postgresql.org/docs/current/pgupgrade.html) utility or a set of recommended practices in the [official docs](https://www.postgresql.org/docs/current/upgrading.html).
- If you're starting a new project on Supabase, you'll already be on PG15.
- If you want to upgrade an existing project, check out our [Upgrading](https://supabase.com/docs/guides/platform/migrating-and-upgrading-projects#upgrade-your-project) documentation.
## More Launch Week 6
- [Day 1: New Supabase Docs, built with Next.js](https://supabase.com/blog/new-supabase-docs-built-with-nextjs)
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Community Day](https://supabase.com/blog/launch-week-6-community-day)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
+6 -3
View File
@@ -98,6 +98,9 @@ Longer term we look forward to experimenting with more ambitions features like a
- [Day 1: New Supabase Docs, built with Next.js](https://supabase.com/blog/new-supabase-docs-built-with-nextjs)
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Launch Week 6 Hackathon](https://supabase.com/blog/launch-week-6-hackathon)
- [Who We Hire at Supabase](https://supabase.com/blog/who-we-hire)
- [pg_crdt - an experimental CRDT extension for Postgres](https://supabase.com/blog/postgres-crdt)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Community Day](https://supabase.com/blog/launch-week-6-community-day)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
@@ -45,6 +45,8 @@ Enabling Point in Time Recovery could be done [in the Dashboard](https://app.sup
- [Day 1: New Supabase Docs, built with Next.js](https://supabase.com/blog/new-supabase-docs-built-with-nextjs)
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Launch Week 6 Hackathon](https://supabase.com/blog/launch-week-6-hackathon)
- [Who We Hire at Supabase](https://supabase.com/blog/who-we-hire)
- [pg_crdt - an experimental CRDT extension for Postgres](https://supabase.com/blog/postgres-crdt)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Community Day](https://supabase.com/blog/launch-week-6-community-day)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
@@ -224,3 +224,15 @@ $ supabase start
```
Please try it and report any bugs, suggestions or ideas!
## More Launch Week 6
- [Day 1: New Supabase Docs, built with Next.js](https://supabase.com/blog/new-supabase-docs-built-with-nextjs)
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Day 5: Supabase Vault is now in Beta](https://supabase.com/blog/vault-now-in-beta)
- [Community Day](https://supabase.com/blog/launch-week-6-community-day)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
@@ -91,6 +91,8 @@ You'll see the Vault appear in the Dashboard over the next month. If you want to
- [Day 1: New Supabase Docs, built with Next.js](https://supabase.com/blog/new-supabase-docs-built-with-nextjs)
- [Day 2: Supabase Storage v2: Image resizing and Smart CDN](https://supabase.com/blog/storage-image-resizing-smart-cdn)
- [Day 3: Multi-factor Authentication via Row Level Security Enforcement](https://supabase.com/blog/mfa-auth-via-rls)
- [Launch Week 6 Hackathon](https://supabase.com/blog/launch-week-6-hackathon)
- [Who We Hire at Supabase](https://supabase.com/blog/who-we-hire)
- [pg_crdt - an experimental CRDT extension for Postgres](https://supabase.com/blog/postgres-crdt)
- [Day 4: Supabase Wrappers, a Postgres FDW framework written in Rust](https://supabase.com/blog/postgres-foreign-data-wrappers-rust)
- [Community Day](https://supabase.com/blog/launch-week-6-community-day)
- [Point in Time Recovery is now available](https://supabase.com/blog/postgres-point-in-time-recovery)
- [Custom Domain Names are now available](https://supabase.com/blog/custom-domain-names)
- [Wrap Up: everything we shipped](https://supabase.com/blog/launch-week-6-wrap-up)
+5 -11
View File
@@ -1,26 +1,20 @@
[
{
"text": "Documentation",
"description": "Everything you need that makes it simple to get started.",
"description": "Everything you need to start building with Supabase.",
"url": "/docs",
"icon": "M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
},
{
"text": "API reference",
"description": "Examples and references for using API libraries.",
"url": "/docs/client/supabase-client",
"icon": "M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"
},
{
"text": "Guides",
"description": "Examples and references for using Supabase.",
"url": "/docs/",
"text": "Changelog",
"description": "See the latest updates and product improvements.",
"url": "/changelog",
"icon": "M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"
},
{
"text": "Careers",
"description": "Join the Supabase team and get involved.",
"url": "https://supabase.com/careers",
"url": "/careers",
"icon": "M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
}
]
+25 -16
View File
@@ -535,16 +535,20 @@ module.exports = [
source: '/docs/reference/postgres/publications',
destination: '/docs/guides/database/replication',
},
{
permanent: true,
source: '/docs/guides/hosting/platform',
destination: '/docs/guides/platform',
},
{
permanent: false,
source: '/docs/guides/self-hosting',
destination: '/docs/guides/hosting/overview',
permanent: true,
source: '/docs/guides/hosting/platform/access-control',
destination: '/docs/guides/platform/access-control',
},
{
permanent: true,
source: '/docs/guides/self-hosting/overview',
destination: '/docs/guides/self-hosting',
},
{
permanent: true,
@@ -1707,6 +1711,11 @@ module.exports = [
source: '/docs/guides/auth/auth-workos',
destination: '/docs/guides/auth/social-login/auth-workos',
},
{
permanent: true,
source: '/docs/guides/auth/auth-zoom',
destination: '/docs/guides/auth/social-login/auth-zoom',
},
{
permanent: true,
source: '/docs/guides/database',
@@ -1787,25 +1796,25 @@ module.exports = [
source: '/docs/guides/with-ionic-angular',
destination: '/docs/guides/getting-started/tutorials/with-ionic-angular',
},
{
permanent: true,
source: '/docs/guides/examples',
destination: '/docs/guides/resources/examples',
},
{
permanent: true,
source: '/docs/guides/examples',
destination: '/docs/guides/resources/examples',
},
{
permanent: true,
source: '/docs/guides/hosting/overview',
destination: '/docs/guides/resources/self-hosting',
destination: '/docs/guides/self-hosting',
},
{
permanent: true,
source: '/docs/guides/hosting/docker',
destination: '/docs/guides/resources/self-hosting/docker',
destination: '/docs/guides/self-hosting/docker',
},
{
permanent: true,
source: '/docs/guides/resources/self-hosting',
destination: '/docs/guides/self-hosting',
},
{
permanent: true,
source: '/docs/guides/resources/self-hosting/docker',
destination: '/docs/guides/self-hosting/docker',
},
{
permanent: true,
+25 -22
View File
@@ -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 = 'Friday'
const liveDay = null
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-[#163837] 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-[#FFFFFF] 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]'
: ''
}`}
>
@@ -219,11 +219,6 @@ export default function launchweek() {
)}
></div>
</div>
<a className="flex items-center justify-center mb-20" href="#currentDay">
<div className="border border-slate-600 text-black dark:text-white text-sm rounded-full bg-slate-400 px-2 py-1 hover:bg-slate-600">
Jump to current day
</div>
</a>
{process.env.NEXT_PUBLIC_LW_STARTED && (
<>
<SectionContainer className="!py-0 ">
@@ -335,7 +330,7 @@ export default function launchweek() {
>
<div className="flex items-center relative z-10 justify-between flex-col-reverse lg:flex-row lg:justify-start gap-2 text-black dark:text-white">
<span>{day1.description}</span>
<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-[#4d898c] dark:from-white dark:to-[#DFFFF1] dark:border-[#DFFFF1]">
<Badge className="!bg-transparent h-fit lg:ml-4 text-sm !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]">
Redesigned
</Badge>
</div>
@@ -380,7 +375,7 @@ export default function launchweek() {
></div>
<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-[#DFFFF1]">
<Badge className="!bg-transparent h-fit lg: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>
@@ -459,7 +454,7 @@ export default function launchweek() {
<div className="flex items-center justify-between flex-col-reverse lg:flex-row lg:justify-start text-black dark:text-white">
<span>{day3.steps[0].title}</span>
<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]">
<Badge className="!bg-transparent h-fit lg: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]">
Updated
</Badge>
</div>
@@ -521,7 +516,7 @@ export default function launchweek() {
</div>
<div className="flex items-center flex-col-reverse lg:flex-row text-black dark:text-white">
<span>{day4.steps[0].title}</span>
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
<Badge className="!bg-transparent h-fit lg:ml-4 text-sm !py-1 !px-4 text-transparent bg-clip-text bg-gradient-to-r from-[#99bbab] to-[#396f55] border-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
New
</Badge>
</div>
@@ -590,7 +585,7 @@ export default function launchweek() {
<span className="text-black dark:text-white">
{day5.steps[0].title}
</span>
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
<Badge className="!bg-transparent h-fit lg:ml-4 text-sm !py-1 !px-4 text-transparent bg-clip-text bg-gradient-to-r from-[#99bbab] to-[#396f55] border-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
New
</Badge>
</div>
@@ -783,7 +778,7 @@ export default function launchweek() {
/>
</div>
<div className="flex flex-col items-center gap-2 min-w-[300px]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
Updated
</Badge>
<span className="text-black dark:text-white text-center">
@@ -828,7 +823,7 @@ export default function launchweek() {
/>
</div>
<div className="flex flex-col items-center gap-2 min-w-[300px]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
New
</Badge>
<span className="text-black dark:text-white text-center">
@@ -873,7 +868,7 @@ export default function launchweek() {
/>
</div>
<div className="flex flex-col items-center gap-2 min-w-[300px]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
New
</Badge>
<span className="text-black dark:text-white text-center">
@@ -918,7 +913,7 @@ export default function launchweek() {
/>
</div>
<div className="flex flex-col items-center gap-2 min-w-[300px]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
Experimental
</Badge>
<span className="text-black dark:text-white text-center">
@@ -963,7 +958,7 @@ export default function launchweek() {
/>
</div>
<div className="flex flex-col items-center gap-2 min-w-[300px]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
Upgrade
</Badge>
<span className="text-black dark:text-white text-center">
@@ -1008,7 +1003,7 @@ export default function launchweek() {
/>
</div>
<div className="flex flex-col items-center gap-2 min-w-[300px]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
<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-[#4d898c] dark:from-white dark:to-[#1a7a4ca1] dark:border-[#DFFFF1]">
Upgrade
</Badge>
<span className="text-black dark:text-white text-center">
@@ -1034,8 +1029,10 @@ export default function launchweek() {
<SectionContainer className="!pt-0 sm:!pb-20 !pb-40"></SectionContainer>
<SectionContainer className="!pt-0 sm:!pb-20 !pb-40">
<div className="flex flex-col mb-16">
<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
className={`w-fit !bg-transparent !py-1 !px-4 text-transparent bg-clip-text bg-gradient-to-r from-[#99bbab] to-[#396f55] dark:from-white dark:to-[#DFFFF1] h-fit relative mb-4 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-[#FFFFFF] 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]`}
>
Submissions Closed
</Badge>
<h2 className="text-3xl text-black dark:text-white mb-2">Launch Week Hackathon</h2>
<p className="text-slate-900 w-[80%] lg:w-[50%]">
@@ -1217,8 +1214,14 @@ 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">
Currently Happening
<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 mb-4 ${
true
? '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-[#FFFFFF] 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]'
: ''
}`}
>
Shipped
</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">
+11 -5
View File
@@ -1,8 +1,8 @@
# Usage
# Start: docker-compose up
# With helpers: docker-compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up
# Stop: docker-compose down
# Destroy: docker-compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down -v --remove-orphans
# Start: docker compose up
# With helpers: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up
# Stop: docker compose down
# Destroy: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down -v --remove-orphans
version: "3.8"
@@ -197,9 +197,15 @@ services:
- log_min_messages=fatal # prevents Realtime polling queries from appearing in logs
restart: unless-stopped
ports:
- ${POSTGRES_PORT}:5432
# Pass down internal port because it's set dynamically by other services
- ${POSTGRES_PORT}:${POSTGRES_PORT}
environment:
POSTGRES_HOST: /var/run/postgresql
PGPORT: ${POSTGRES_PORT}
POSTGRES_PORT: ${POSTGRES_PORT}
PGPASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
PGDATABASE: ${POSTGRES_DB}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/roles.sql
+1 -1
View File
@@ -1,5 +1,5 @@
-- NOTE: change to your own passwords for production environments
\set pgpass `echo "$PGPASSWORD"`
\set pgpass `echo "$POSTGRES_PASSWORD"`
ALTER USER authenticator WITH PASSWORD :'pgpass';
ALTER USER pgbouncer WITH PASSWORD :'pgpass';
File diff suppressed because it is too large. Load diff
@@ -7486,9 +7486,9 @@
}
},
"node_modules/decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==",
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"engines": {
"node": ">=0.10"
}
@@ -25876,9 +25876,9 @@
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
},
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og=="
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
},
"deep-equal": {
"version": "1.1.1",
@@ -5135,9 +5135,9 @@
}
},
"node_modules/decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"engines": {
"node": ">=0.10"
}
@@ -17870,9 +17870,9 @@
}
},
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
},
"deepmerge": {
"version": "4.2.2",
@@ -6397,9 +6397,9 @@
"integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw=="
},
"node_modules/decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"engines": {
"node": ">=0.10"
}
@@ -26699,9 +26699,9 @@
"integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw=="
},
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
},
"dedent": {
"version": "0.7.0",
+128 -11
View File
@@ -71,17 +71,134 @@ Supabase es una [plataforma alojada](https://app.supabase.com). Te puedes regist
Nuestra librería de cliente es modular. Cada sub-librería es una implementación independiente para cada sistema externo. Esta es una de las formas de apoyar las herramientas existentes.
- **`supabase-{lang}`**: Combina librerías y agrega mejoras.
- `postgrest-{lang}`: Librería de cliente para trabajar con [PostgREST](https://github.com/postgrest/postgrest)
- `realtime-{lang}`: Librería de cliente para trabajar con [Realtime](https://github.com/supabase/realtime)
- `gotrue-{lang}`: Librería de cliente para trabajar con [GoTrue](https://github.com/netlify/gotrue)
| Repositorio | Oficial | Comunidad |
| --------------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`supabase-{lang}`** | [`JS`](https://github.com/supabase/supabase-js) | [`C#`](https://github.com/supabase/supabase-csharp) \| [`Flutter`](https://github.com/supabase/supabase-flutter) \| [`Python`](https://github.com/supabase/supabase-py) \| `Rust` |
| `postgrest-{lang}` | [`JS`](https://github.com/supabase/postgrest-js) | [`C#`](https://github.com/supabase/postgrest-csharp) \| [`Dart`](https://github.com/supabase/postgrest-dart) \| [`Python`](https://github.com/supabase/postgrest-py) \| [`Rust`](https://github.com/supabase/postgrest-rs) |
| `realtime-{lang}` | [`JS`](https://github.com/supabase/realtime-js) | [`C#`](https://github.com/supabase/realtime-csharp) \| [`Dart`](https://github.com/supabase/realtime-dart) \| [`Python`](https://github.com/supabase/realtime-py) \| `Rust` |
| `gotrue-{lang}` | [`JS`](https://github.com/supabase/gotrue-js) | [`C#`](https://github.com/supabase/gotrue-csharp) \| [`Dart`](https://github.com/supabase/gotrue-dart) \| [`Python`](https://github.com/supabase/gotrue-py) \| `Rust` |
<table style="table-layout:fixed; white-space: nowrap;">
<tr>
<th>Lenguaje</th>
<th>Cliente</th>
<th colspan="5">Característica - Clientes (incluido en el cliente de Supabase)</th>
</tr>
<tr>
<th></th>
<th>Supabase</th>
<th><a href="https://github.com/postgrest/postgrest" target="_blank" rel="noopener noreferrer">PostgREST</a></th>
<th><a href="https://github.com/supabase/gotrue" target="_blank" rel="noopener noreferrer">GoTrue</a></th>
<th><a href="https://github.com/supabase/realtime" target="_blank" rel="noopener noreferrer">Realtime</a></th>
<th><a href="https://github.com/supabase/storage-api" target="_blank" rel="noopener noreferrer">Storage</a></th>
<th>Functions</th>
</tr>
<!-- TEMPLATE FOR NEW ROW -->
<!-- START ROW
<tr>
<td>lang</td>
<td><a href="https://github.com/supabase-community/supabase-lang" target="_blank" rel="noopener noreferrer">supabase-lang</a></td>
<td><a href="https://github.com/supabase-community/postgrest-lang" target="_blank" rel="noopener noreferrer">postgrest-lang</a></td>
<td><a href="https://github.com/supabase-community/gotrue-lang" target="_blank" rel="noopener noreferrer">gotrue-lang</a></td>
<td><a href="https://github.com/supabase-community/realtime-lang" target="_blank" rel="noopener noreferrer">realtime-lang</a></td>
<td><a href="https://github.com/supabase-community/storage-lang" target="_blank" rel="noopener noreferrer">storage-lang</a></td>
</tr>
END ROW -->
<th colspan="7">⚡️ Oficial ⚡️</th>
<tr>
<td>JavaScript (TypeScript)</td>
<td><a href="https://github.com/supabase/supabase-js" target="_blank" rel="noopener noreferrer">supabase-js</a></td>
<td><a href="https://github.com/supabase/postgrest-js" target="_blank" rel="noopener noreferrer">postgrest-js</a></td>
<td><a href="https://github.com/supabase/gotrue-js" target="_blank" rel="noopener noreferrer">gotrue-js</a></td>
<td><a href="https://github.com/supabase/realtime-js" target="_blank" rel="noopener noreferrer">realtime-js</a></td>
<td><a href="https://github.com/supabase/storage-js" target="_blank" rel="noopener noreferrer">storage-js</a></td>
<td><a href="https://github.com/supabase/functions-js" target="_blank" rel="noopener noreferrer">functions-js</a></td>
</tr>
<tr>
<td>Flutter</td>
<td><a href="https://github.com/supabase/supabase-flutter" target="_blank" rel="noopener noreferrer">supabase-flutter</a></td>
<td><a href="https://github.com/supabase/postgrest-dart" target="_blank" rel="noopener noreferrer">postgrest-dart</a></td>
<td><a href="https://github.com/supabase/gotrue-dart" target="_blank" rel="noopener noreferrer">gotrue-dart</a></td>
<td><a href="https://github.com/supabase/realtime-dart" target="_blank" rel="noopener noreferrer">realtime-dart</a></td>
<td><a href="https://github.com/supabase/storage-dart" target="_blank" rel="noopener noreferrer">storage-dart</a></td>
<td><a href="https://github.com/supabase/functions-dart" target="_blank" rel="noopener noreferrer">functions-dart</a></td>
</tr>
<th colspan="7">💚 Comunidad 💚</th>
<tr>
<td>C#</td>
<td><a href="https://github.com/supabase-community/supabase-csharp" target="_blank" rel="noopener noreferrer">supabase-csharp</a></td>
<td><a href="https://github.com/supabase-community/postgrest-csharp" target="_blank" rel="noopener noreferrer">postgrest-csharp</a></td>
<td><a href="https://github.com/supabase-community/gotrue-csharp" target="_blank" rel="noopener noreferrer">gotrue-csharp</a></td>
<td><a href="https://github.com/supabase-community/realtime-csharp" target="_blank" rel="noopener noreferrer">realtime-csharp</a></td>
<td><a href="https://github.com/supabase-community/storage-csharp" target="_blank" rel="noopener noreferrer">storage-csharp</a></td>
<td><a href="https://github.com/supabase-community/functions-csharp" target="_blank" rel="noopener noreferrer">functions-csharp</a></td>
</tr>
<tr>
<td>Go</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/postgrest-go" target="_blank" rel="noopener noreferrer">postgrest-go</a></td>
<td><a href="https://github.com/supabase-community/gotrue-go" target="_blank" rel="noopener noreferrer">gotrue-go</a></td>
<td>-</td>
<td><a href="https://github.com/supabase-community/storage-go" target="_blank" rel="noopener noreferrer">storage-go</a></td>
<td><a href="https://github.com/supabase-community/functions-go" target="_blank" rel="noopener noreferrer">functions-go</a></td>
</tr>
<tr>
<td>Java</td>
<td>-</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/gotrue-java" target="_blank" rel="noopener noreferrer">gotrue-java</a></td>
<td>-</td>
<td><a href="https://github.com/supabase-community/storage-java" target="_blanke" rel="noopener noreferrer">storage-java</a></td>
<td>-</td>
</tr>
<tr>
<td>Kotlin</td>
<td><a href="https://github.com/supabase-community/supabase-kt" target="_blank" rel="noopener noreferrer">supabase-kt</a></td>
<td><a href="https://github.com/supabase-community/postgrest-kt" target="_blank" rel="noopener noreferrer">postgrest-kt</a></td>
<td><a href="https://github.com/supabase-community/gotrue-kt" target="_blank" rel="noopener noreferrer">gotrue-kt</a></td>
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/Realtime" target="_blank" rel="noopener noreferrer">realtime-kt</a></td>
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/Storage" target="_blank" rel="noopener noreferrer">storage-kt</a></td>
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/Functions" target="_blank" rel="noopener noreferrer">functions-kt</a></td>
</tr>
<tr>
<td>Python</td>
<td><a href="https://github.com/supabase-community/supabase-py" target="_blank" rel="noopener noreferrer">supabase-py</a></td>
<td><a href="https://github.com/supabase-community/postgrest-py" target="_blank" rel="noopener noreferrer">postgrest-py</a></td>
<td><a href="https://github.com/supabase-community/gotrue-py" target="_blank" rel="noopener noreferrer">gotrue-py</a></td>
<td><a href="https://github.com/supabase-community/realtime-py" target="_blank" rel="noopener noreferrer">realtime-py</a></td>
<td><a href="https://github.com/supabase-community/storage-py" target="_blank" rel="noopener noreferrer">storage-py</a></td>
<td><a href="https://github.com/supabase-community/functions-py" target="_blank" rel="noopener noreferrer">functions-py</a></td>
</tr>
<tr>
<td>Ruby</td>
<td><a href="https://github.com/supabase-community/supabase-rb" target="_blank" rel="noopener noreferrer">supabase-rb</a></td>
<td><a href="https://github.com/supabase-community/postgrest-rb" target="_blank" rel="noopener noreferrer">postgrest-rb</a></td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<td>Rust</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/postgrest-rs" target="_blank" rel="noopener noreferrer">postgrest-rs</a></td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<td>Swift</td>
<td><a href="https://github.com/supabase-community/supabase-swift" target="_blank" rel="noopener noreferrer">supabase-swift</a></td>
<td><a href="https://github.com/supabase-community/postgrest-swift" target="_blank" rel="noopener noreferrer">postgrest-swift</a></td>
<td><a href="https://github.com/supabase-community/gotrue-swift" target="_blank" rel="noopener noreferrer">gotrue-swift</a></td>
<td><a href="https://github.com/supabase-community/realtime-swift" target="_blank" rel="noopener noreferrer">realtime-swift</a></td>
<td><a href="https://github.com/supabase-community/storage-swift" target="_blank" rel="noopener noreferrer">storage-swift</a></td>
<td><a href="https://github.com/supabase-community/functions-swift" target="_blank" rel="noopener noreferrer">functions-swift</a></td>
</tr>
<tr>
<td>Godot Engine (GDScript)</td>
<td><a href="https://github.com/supabase-community/godot-engine.supabase" target="_blank" rel="noopener noreferrer">supabase-gdscript</a></td>
<td><a href="https://github.com/supabase-community/postgrest-gdscript" target="_blank" rel="noopener noreferrer">postgrest-gdscript</a></td>
<td><a href="https://github.com/supabase-community/gotrue-gdscript" target="_blank" rel="noopener noreferrer">gotrue-gdscript</a></td>
<td><a href="https://github.com/supabase-community/realtime-gdscript" target="_blank" rel="noopener noreferrer">realtime-gdscript</a></td>
<td><a href="https://github.com/supabase-community/storage-gdscript" target="_blank" rel="noopener noreferrer">storage-gdscript</a></td>
<td><a href="https://github.com/supabase-community/functions-gdscript" target="_blank" rel="noopener noreferrer">functions-gdscript</a></td>
</tr>
</table>
<!--- Remove this list if you're traslating to another language, it's hard to keep updated across multiple files-->
<!--- Keep only the link to the list of translation files-->
Loaded 100 of 198 files, more files were not shown because too many files have changed in this diff. Show more