mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
Merge branch 'master' into thor/blog-chatgpt-plugin-template
This commit is contained in:
612 files changed
+18613
-7209
No files matched your search
@@ -11,9 +11,16 @@ jobs:
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
- name: Download dependencies
|
||||
run: npm ci
|
||||
- name: Run prettier
|
||||
run: |-
|
||||
npx prettier -c 'apps/**/*.{js,jsx,ts,tsx,css,md,json}'
|
||||
npx prettier -c 'apps/**/*.{js,jsx,ts,tsx,css,md,mdx,json}'
|
||||
# [Joshen] Temp disable the following due to an issue
|
||||
# https://github.com/EPMatt/reviewdog-action-prettier/issues/34
|
||||
# uses: EPMatt/reviewdog-action-prettier@v1
|
||||
@@ -42,9 +49,16 @@ jobs:
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'npm'
|
||||
- name: Download dependencies
|
||||
run: npm ci
|
||||
- name: Run prettier
|
||||
run: |-
|
||||
npx prettier -c 'i18n/**/*.{js,jsx,ts,tsx,css,md,json}'
|
||||
npx prettier -c 'i18n/**/*.{js,jsx,ts,tsx,css,md,mdx,json}'
|
||||
# uses: EPMatt/reviewdog-action-prettier@v1
|
||||
# with:
|
||||
# github_token: ${{ secrets.github_token }}
|
||||
|
||||
@@ -9,7 +9,12 @@ on:
|
||||
- 'supabase/migrations/**'
|
||||
- 'apps/docs/**'
|
||||
- 'spec/**'
|
||||
workflow_dispatch: {}
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
refresh:
|
||||
description: 'Refresh all pages'
|
||||
required: false
|
||||
type: boolean
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
@@ -46,6 +51,12 @@ jobs:
|
||||
- name: Run migrations
|
||||
run: npx supabase db push
|
||||
|
||||
- name: Update embeddings
|
||||
working-directory: ./apps/docs
|
||||
if: ${{ !inputs.refresh }}
|
||||
run: npm run embeddings
|
||||
|
||||
- name: Refresh embeddings
|
||||
working-directory: ./apps/docs
|
||||
run: npm run embeddings
|
||||
if: ${{ inputs.refresh }}
|
||||
run: npm run embeddings:refresh
|
||||
+3
-1
@@ -5,5 +5,7 @@
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"endOfLine": "lf",
|
||||
"sqlKeywordCase": "lower"
|
||||
"sqlKeywordCase": "lower",
|
||||
"pluginSearchDirs": false,
|
||||
"plugins": ["prettier-plugin-sql-cst"]
|
||||
}
|
||||
@@ -149,8 +149,8 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
|
||||
<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/Postgrest" target="_blank" rel="noopener noreferrer">postgrest-kt</a></td>
|
||||
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/GoTrue" 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>
|
||||
|
||||
@@ -79,7 +79,7 @@ export const unHighlightSelectedTocItems = () => {
|
||||
}
|
||||
|
||||
export const highlightSelectedNavItem = (id: string) => {
|
||||
const navMenuItems = document.querySelectorAll('.function-link-item a')
|
||||
const navMenuItems = document.querySelectorAll<HTMLAnchorElement>('.function-link-item a')
|
||||
|
||||
// find any currently active items and remove them
|
||||
const currentActiveItems = document.querySelectorAll('.function-link-list .text-brand-900')
|
||||
@@ -87,7 +87,6 @@ export const highlightSelectedNavItem = (id: string) => {
|
||||
|
||||
// Add active class to the current item
|
||||
navMenuItems.forEach((item) => {
|
||||
// @ts-ignore
|
||||
if (item.href.split('/').at(-1) === id) {
|
||||
item.classList.add('text-brand-900')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,497 @@
|
||||
import React from 'react'
|
||||
import { LazyMotion, domAnimation, m } from 'framer-motion'
|
||||
|
||||
const DocsCoverLogo = () => {
|
||||
const pathMotionConfig = {
|
||||
initial: { pathLength: 0 },
|
||||
animate: { pathLength: 1 },
|
||||
transition: {
|
||||
duration: 1,
|
||||
delay: 0.2,
|
||||
ease: [0.5, 0.11, 0.13, 1],
|
||||
},
|
||||
}
|
||||
const logoMotionConfig = {
|
||||
initial: { fillOpacity: 0 },
|
||||
animate: { fillOpacity: 1 },
|
||||
transition: {
|
||||
duration: 1,
|
||||
delay: 0.5,
|
||||
ease: [0.25, 0.25, 0, 1],
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-[60px] md:w-[150px] [&_svg]">
|
||||
<LazyMotion features={domAnimation}>
|
||||
<m.svg
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 250 228"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M124.951 5.03906V224.602"
|
||||
stroke="url(#paint0_radial_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M125.25 191.383C167.828 191.383 202.344 156.867 202.344 114.289C202.344 71.7115 167.828 37.1953 125.25 37.1953C82.6724 37.1953 48.1562 71.7115 48.1562 114.289C48.1562 156.867 82.6724 191.383 125.25 191.383Z"
|
||||
stroke="var(--colors-scale11)"
|
||||
stroke-opacity="0.1"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M33.7782 58.8377C48.6666 58.8377 60.7361 46.7682 60.7361 31.8798C60.7361 16.9913 48.6666 4.92188 33.7782 4.92188C18.8898 4.92188 6.82031 16.9913 6.82031 31.8798C6.82031 46.7682 18.8898 58.8377 33.7782 58.8377Z"
|
||||
stroke="url(#paint1_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M33.7782 223.664C48.6666 223.664 60.7361 211.594 60.7361 196.706C60.7361 181.817 48.6666 169.748 33.7782 169.748C18.8898 169.748 6.82031 181.817 6.82031 196.706C6.82031 211.594 18.8898 223.664 33.7782 223.664Z"
|
||||
stroke="url(#paint2_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M216.724 58.8377C231.612 58.8377 243.681 46.7682 243.681 31.8798C243.681 16.9913 231.612 4.92188 216.724 4.92188C201.835 4.92188 189.766 16.9913 189.766 31.8798C189.766 46.7682 201.835 58.8377 216.724 58.8377Z"
|
||||
stroke="url(#paint3_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M216.724 223.664C231.612 223.664 243.681 211.594 243.681 196.706C243.681 181.817 231.612 169.748 216.724 169.748C201.835 169.748 189.766 181.817 189.766 196.706C189.766 211.594 201.835 223.664 216.724 223.664Z"
|
||||
stroke="url(#paint4_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M209.29 226.982H40.6907C18.7665 226.982 1 209.51 1 187.949V40.0333C1 18.4722 18.7665 1 40.6907 1H209.29C231.214 1 248.981 18.4722 248.981 40.0333V187.969C248.981 209.51 231.214 226.982 209.29 226.982Z"
|
||||
stroke="url(#paint5_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M216.724 223.903H33.7779C18.719 223.903 6.5 211.684 6.5 196.625V31.9576C6.5 16.8987 18.719 4.67969 33.7779 4.67969H216.724C231.782 4.67969 244.001 16.8987 244.001 31.9576V196.625C244.001 211.704 231.802 223.903 216.724 223.903Z"
|
||||
stroke="url(#paint6_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M60.7578 30.9199V197.027"
|
||||
stroke="url(#paint7_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M125.252 5.01953V223.562"
|
||||
stroke="url(#paint8_radial_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M149.809 4.83984L13.5996 178.866"
|
||||
stroke="url(#paint9_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M239.679 46.457L99.5703 223.643"
|
||||
stroke="url(#paint10_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M189.766 30.9199V197.027"
|
||||
stroke="url(#paint11_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M218.024 58.7344H32.498"
|
||||
stroke="url(#paint12_radial_0_1)"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M6.76172 95.4141H243.743"
|
||||
stroke="url(#paint13_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M6.76172 131.869H243.743"
|
||||
stroke="url(#paint14_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M218.024 169.846H32.498"
|
||||
stroke="var(--colors-scale11)"
|
||||
stroke-opacity="0.1"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M126.559 4.6867L126.559 224.254"
|
||||
stroke="url(#paint15_linear_0_1)"
|
||||
stroke-opacity="0.5"
|
||||
stroke-width="0.7"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
/>
|
||||
<g filter="url(#filter0_d_0_1)">
|
||||
<m.path
|
||||
initial={logoMotionConfig.initial}
|
||||
animate={logoMotionConfig.animate}
|
||||
transition={logoMotionConfig.transition as any}
|
||||
d="M125.751 174.003C125.833 179.464 134.091 181.807 137.49 177.53L188.786 112.985C194.84 105.367 189.41 94.1317 179.674 94.1317H126.964L127.046 52.5389C126.964 47.078 119.027 44.7344 115.628 49.0116L65.5801 113.556C59.5255 121.175 64.9558 132.41 74.6925 132.41H125.161L125.751 174.003Z"
|
||||
fill="url(#paint16_linear_0_1)"
|
||||
fill-opacity="0.79"
|
||||
shape-rendering="crispEdges"
|
||||
/>
|
||||
<m.path
|
||||
initial={pathMotionConfig.initial}
|
||||
animate={pathMotionConfig.animate}
|
||||
transition={pathMotionConfig.transition as any}
|
||||
d="M126.964 94.6797L126.416 94.1306L126.498 52.5425C126.459 50.117 124.68 48.3327 122.422 47.6478C120.162 46.9621 117.586 47.432 116.059 49.3499C116.059 49.3508 116.058 49.3516 116.057 49.3525L66.0131 113.892L66.0091 113.897C60.2405 121.156 65.4135 131.862 74.6925 131.862H125.161L125.709 132.402L126.299 173.995L126.299 173.995M126.964 94.6797L125.751 174.003L126.299 173.995M126.964 94.6797H179.674C188.953 94.6797 194.126 105.386 188.357 112.644L137.061 177.189C135.539 179.104 132.883 179.583 130.531 178.892C128.18 178.202 126.336 176.41 126.299 173.995M126.964 94.6797L126.299 173.995"
|
||||
stroke="url(#paint17_linear_0_1)"
|
||||
stroke-width="1.09591"
|
||||
stroke-miterlimit="10"
|
||||
stroke-linejoin="bevel"
|
||||
shape-rendering="crispEdges"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_d_0_1"
|
||||
x="58.6515"
|
||||
y="46.8027"
|
||||
width="137.062"
|
||||
height="141.703"
|
||||
filterUnits="userSpaceOnUse"
|
||||
color-interpolation-filters="sRGB"
|
||||
>
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
<feColorMatrix
|
||||
in="SourceAlpha"
|
||||
type="matrix"
|
||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||
result="hardAlpha"
|
||||
/>
|
||||
<feOffset dy="4.38364" />
|
||||
<feGaussianBlur stdDeviation="2.19182" />
|
||||
<feComposite in2="hardAlpha" operator="out" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_1" />
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="effect1_dropShadow_0_1"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
<radialGradient
|
||||
id="paint0_radial_0_1"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(125.451 114.82) rotate(90) scale(109.781 0.5)"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
</radialGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_0_1"
|
||||
x1="38.9975"
|
||||
y1="58.9958"
|
||||
x2="18.999"
|
||||
y2="11.9994"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_0_1"
|
||||
x1="43.9971"
|
||||
y1="168.986"
|
||||
x2="32.998"
|
||||
y2="204.984"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint3_linear_0_1"
|
||||
x1="221.943"
|
||||
y1="58.9958"
|
||||
x2="231.981"
|
||||
y2="13.9993"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint4_linear_0_1"
|
||||
x1="203.983"
|
||||
y1="168.986"
|
||||
x2="215.943"
|
||||
y2="204.984"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint5_linear_0_1"
|
||||
x1="121.402"
|
||||
y1="227.645"
|
||||
x2="121.402"
|
||||
y2="-7.94661"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
<stop offset="0.489583" stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint6_linear_0_1"
|
||||
x1="121.814"
|
||||
y1="224.545"
|
||||
x2="121.814"
|
||||
y2="-3.99932"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
<stop offset="0.489583" stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint7_linear_0_1"
|
||||
x1="60.7578"
|
||||
y1="30.3703"
|
||||
x2="60.7578"
|
||||
y2="199.061"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale10)" stop-opacity="0.1" />
|
||||
<stop offset="0.505208" stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0.1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
id="paint8_radial_0_1"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(125.752 114.291) rotate(90) scale(109.271 0.5)"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
</radialGradient>
|
||||
<linearGradient
|
||||
id="paint9_linear_0_1"
|
||||
x1="6.99856"
|
||||
y1="0"
|
||||
x2="200"
|
||||
y2="6.31757"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale11)" stop-opacity="0.3" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint10_linear_0_1"
|
||||
x1="200"
|
||||
y1="100"
|
||||
x2="0"
|
||||
y2="200"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale11)" stop-opacity="0.3" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint11_linear_0_1"
|
||||
x1="189.766"
|
||||
y1="30.3703"
|
||||
x2="189.766"
|
||||
y2="199.061"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale10)" stop-opacity="0.1" />
|
||||
<stop offset="0.505208" stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0.1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
id="paint12_radial_0_1"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(125.261 59.2344) rotate(90) scale(0.5 263.749)"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0" />
|
||||
</radialGradient>
|
||||
<linearGradient
|
||||
id="paint13_linear_0_1"
|
||||
x1="6.99998"
|
||||
y1="94.9749"
|
||||
x2="242.982"
|
||||
y2="94.9749"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale10)" stop-opacity="0.5" />
|
||||
<stop offset="0.505208" stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint14_linear_0_1"
|
||||
x1="6.99998"
|
||||
y1="131.43"
|
||||
x2="242.982"
|
||||
y2="131.43"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale10)" stop-opacity="0.5" />
|
||||
<stop offset="0.505208" stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0.5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint15_linear_0_1"
|
||||
x1="126.559"
|
||||
y1="224.98"
|
||||
x2="126.559"
|
||||
y2="1.99785"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="var(--colors-scale10)" stop-opacity="0.1" />
|
||||
<stop offset="0.505208" stop-color="var(--colors-scale11)" stop-opacity="0.5" />
|
||||
<stop offset="1" stop-color="var(--colors-scale10)" stop-opacity="0.1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint16_linear_0_1"
|
||||
x1="155.11"
|
||||
y1="46.8027"
|
||||
x2="126.979"
|
||||
y2="206.049"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#212121" />
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.12" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint17_linear_0_1"
|
||||
x1="191.328"
|
||||
y1="82.7684"
|
||||
x2="103.041"
|
||||
y2="175.889"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#868585" />
|
||||
<stop offset="0.208333" stop-color="#838383" />
|
||||
<stop offset="1" stop-color="#5A5A5A" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</m.svg>
|
||||
</LazyMotion>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DocsCoverLogo
|
||||
@@ -6,6 +6,12 @@ const examples = [
|
||||
description: 'Use the Supabase client inside your Edge Function.',
|
||||
href: 'https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/select-from-table-with-auth-rls/index.ts',
|
||||
},
|
||||
{
|
||||
name: 'Type-Safe SQL with Kysely',
|
||||
description:
|
||||
'Combining Kysely with Deno Postgres gives you a convenient developer experience for interacting directly with your Postgres database.',
|
||||
href: '/guides/functions/kysely-postgres',
|
||||
},
|
||||
{
|
||||
name: 'With CORS headers',
|
||||
description: 'Send CORS headers for invoking from the browser.',
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import Link from 'next/link'
|
||||
import { IconBackground, IconPanel, IconPlay } from 'ui'
|
||||
import { useBreakpoint } from 'common'
|
||||
import DocsCoverLogo from './DocsCoverLogo'
|
||||
|
||||
const HomePageCover = (props) => {
|
||||
const isXs = useBreakpoint(639)
|
||||
const iconSize = isXs ? 'sm' : 'lg'
|
||||
|
||||
const GettingStarted = () => (
|
||||
<div
|
||||
className="
|
||||
border border-scale-400 bg-scale-200
|
||||
relative overflow-hidden
|
||||
grid grid-cols-12
|
||||
rounded-lg
|
||||
p-5 md:p-8
|
||||
"
|
||||
>
|
||||
<div className="col-span-full flex flex-col md:flex-row xl:flex-col justify-between gap-1 md:gap-6">
|
||||
<div className="md:max-w-xs xl:max-w-none">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<IconBackground>
|
||||
<IconPlay className="text-brand-1100 dark:text-brand-900 w-4" />
|
||||
</IconBackground>
|
||||
<h3 className="text-2xl m-0">Getting Started</h3>
|
||||
</div>
|
||||
<p className="text-scale-1100 text-sm">
|
||||
Discover how to set up a database to an app making queries in just a few minutes.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap md:grid md:grid-cols-4 gap-2 sm:gap-3">
|
||||
<Link href={`/guides/getting-started/quickstarts/reactjs`} passHref>
|
||||
<a className="no-underline">
|
||||
<IconPanel
|
||||
iconSize={iconSize}
|
||||
hideArrow
|
||||
tooltip="ReactJS"
|
||||
icon="/docs/img/icons/react-icon"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href={`/guides/getting-started/quickstarts/nextjs`} passHref>
|
||||
<a className="no-underline">
|
||||
<IconPanel
|
||||
iconSize={iconSize}
|
||||
hideArrow
|
||||
tooltip="NextJS"
|
||||
icon="/docs/img/icons/nextjs-icon"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href={`/guides/getting-started/quickstarts/redwoodjs`} passHref>
|
||||
<a className="no-underline">
|
||||
<IconPanel
|
||||
iconSize={iconSize}
|
||||
hideArrow
|
||||
tooltip="RedwoodJS"
|
||||
icon="/docs/img/icons/redwoodjs-icon"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href={`/guides/getting-started/quickstarts/flutter`} passHref>
|
||||
<a className="no-underline">
|
||||
<IconPanel
|
||||
iconSize={iconSize}
|
||||
hideArrow
|
||||
tooltip="Flutter"
|
||||
icon="/docs/img/icons/flutter-icon"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href={`/guides/getting-started/quickstarts/sveltekit`} passHref>
|
||||
<a className="no-underline">
|
||||
<IconPanel
|
||||
iconSize={iconSize}
|
||||
hideArrow
|
||||
tooltip="SvelteKit"
|
||||
icon="/docs/img/icons/svelte-icon"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href={`/guides/getting-started/quickstarts/solidjs`} passHref>
|
||||
<a className="no-underline">
|
||||
<IconPanel
|
||||
iconSize={iconSize}
|
||||
hideArrow
|
||||
tooltip="SolidJS"
|
||||
icon="/docs/img/icons/solidjs-icon"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href={`/guides/getting-started/quickstarts/vue`} passHref>
|
||||
<a className="no-underline">
|
||||
<IconPanel
|
||||
iconSize={iconSize}
|
||||
hideArrow
|
||||
tooltip="Vue"
|
||||
icon="/docs/img/icons/vuejs-icon"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="w-full bg-scale-100 border-b prose dark:prose-dar max-w-none mb-16 md:mb-12 xl:mb-0">
|
||||
<div className="max-w-7xl px-5 mx-auto py-8 sm:pb-16 sm:pt-12 xl:pt-16 flex flex-col xl:flex-row justify-between gap-12 xl:gap-12">
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-8 items-start sm:items-center w-full max-w-xl">
|
||||
<DocsCoverLogo />
|
||||
<div className="flex flex-col">
|
||||
<h1 className="m-0 mb-3 text-2xl sm:text-3xl">{props.meta?.title}</h1>
|
||||
<p className="m-0 text-scale-1100">
|
||||
Learn how to get up and running with Supabase through tutorials, APIs and platform
|
||||
resources.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full xl:max-w-[365px] -mb-40">
|
||||
<GettingStarted />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HomePageCover
|
||||
@@ -3,4 +3,4 @@
|
||||
- Click on [`Providers`](https://app.supabase.com/project/_/auth/providers) under the Configuration section
|
||||
- Click on **{props.provider}** from the accordion list to expand and turn **{props.provider} Enabled** to ON
|
||||
- Enter your **{props.provider} Client ID** and **{props.provider} Client Secret** saved in the previous step
|
||||
- Click `Save`
|
||||
- Click `Save`
|
||||
@@ -1,68 +1,163 @@
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Button, IconGitHub, IconTwitter, IconYoutube } from '~/../../packages/ui'
|
||||
import footerData from '~/data/footer.json'
|
||||
import { useTheme } from 'common/Providers'
|
||||
import { Button } from '~/../../packages/ui'
|
||||
import { primaryLinks, secondaryLinks } from '~/data/footer'
|
||||
import { LayoutMainContent } from '../../layouts/DefaultLayout'
|
||||
|
||||
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 ">
|
||||
<Link href="https://supabase.com/">
|
||||
<a className="text-xs text-scale-900">© Supabase Inc</a>
|
||||
</Link>
|
||||
<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://youtube.com/c/supabase"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<IconYoutube size={16} />
|
||||
</Button>
|
||||
<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
|
||||
const Footer = () => (
|
||||
<LayoutMainContent className="pt-0">
|
||||
<div className="mt-16">
|
||||
<ul className="flex flex-col gap-2">
|
||||
{primaryLinks.map(({ url, featherIcon: Icon, icon, text, ctaLabel }) => (
|
||||
<li key={url} className="flex items-center gap-1 text-xs text-scale-900">
|
||||
{icon && (
|
||||
<svg
|
||||
width="16"
|
||||
height="17"
|
||||
viewBox="0 0 16 17"
|
||||
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"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d={icon}
|
||||
stroke="var(--colors-scale9)"
|
||||
strokeWidth={0.7}
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
{Icon && <Icon width={16} height={16} />}
|
||||
<p>{text}</p>
|
||||
<Link href={url} passHref>
|
||||
<a
|
||||
className="text-brand-900 hover:underline"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
{ctaLabel}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<hr className="border-scale-400 my-6"></hr>
|
||||
<div className="flex gap-4 items-center justify-between">
|
||||
<div className="flex flex-col lg:flex-row gap-3 ">
|
||||
<Link href="https://supabase.com/">
|
||||
<a className="text-xs text-scale-900">© Supabase Inc</a>
|
||||
</Link>
|
||||
<span className="text-xs text-scale-900">—</span>
|
||||
{secondaryLinks.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://github.com/supabase/supabase"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<svg
|
||||
width="17"
|
||||
height="17"
|
||||
viewBox="0 0 17 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M8.66832 1.55469C4.98649 1.55469 2.00195 4.54402 2.00195 8.23269C2.00195 11.1827 3.91187 13.686 6.56108 14.5687C6.8944 14.63 7.01573 14.424 7.01573 14.2467C7.01573 14.0887 7.0104 13.668 7.00706 13.1114C5.15248 13.5147 4.76116 12.216 4.76116 12.216C4.45851 11.444 4.0212 11.2387 4.0212 11.2387C3.41589 10.8254 4.06719 10.8334 4.06719 10.8334C4.73583 10.88 5.08782 11.5214 5.08782 11.5214C5.68246 12.5414 6.64841 12.2467 7.02773 12.076C7.08906 11.6447 7.26105 11.3507 7.45171 11.184C5.97178 11.0154 4.41518 10.442 4.41518 7.88335C4.41518 7.15469 4.67517 6.55802 5.10115 6.09135C5.03248 5.92269 4.80383 5.24335 5.16648 4.32469C5.16648 4.32469 5.72645 4.14469 6.99973 5.00869C7.54355 4.86036 8.10464 4.78482 8.66832 4.78402C9.23496 4.78669 9.80494 4.86069 10.3376 5.00869C11.6102 4.14469 12.1688 4.32402 12.1688 4.32402C12.5328 5.24335 12.3035 5.92269 12.2355 6.09135C12.6621 6.55802 12.9208 7.15469 12.9208 7.88335C12.9208 10.4487 11.3615 11.0134 9.87694 11.1787C10.1163 11.3847 10.3289 11.792 10.3289 12.4154C10.3289 13.3074 10.3209 14.028 10.3209 14.2467C10.3209 14.4254 10.4409 14.6334 10.7796 14.568C12.107 14.1228 13.261 13.2716 14.0784 12.1347C14.8958 10.9979 15.3353 9.6329 15.3347 8.23269C15.3347 4.54402 12.3495 1.55469 8.66832 1.55469Z"
|
||||
fill="var(--colors-scale9)"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
as="a"
|
||||
// @ts-ignore
|
||||
href="https://youtube.com/c/supabase"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="17"
|
||||
viewBox="0 0 16 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M15.665 4.35595C15.481 3.66308 14.9388 3.11739 14.2505 2.93221C13.0028 2.5957 7.99964 2.5957 7.99964 2.5957C7.99964 2.5957 2.99655 2.5957 1.74883 2.93221C1.06047 3.11742 0.518326 3.66308 0.334321 4.35595C0 5.61181 0 8.23207 0 8.23207C0 8.23207 0 10.8523 0.334321 12.1082C0.518326 12.8011 1.06047 13.324 1.74883 13.5092C2.99655 13.8457 7.99964 13.8457 7.99964 13.8457C7.99964 13.8457 13.0027 13.8457 14.2505 13.5092C14.9388 13.324 15.481 12.8011 15.665 12.1082C15.9993 10.8523 15.9993 8.23207 15.9993 8.23207C15.9993 8.23207 15.9993 5.61181 15.665 4.35595ZM6.36334 10.6111V5.85307L10.545 8.23212L6.36334 10.6111Z"
|
||||
fill="var(--colors-scale9)"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
as="a"
|
||||
// @ts-ignore
|
||||
href="https://twitter.com/supabase"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<svg
|
||||
width="17"
|
||||
height="17"
|
||||
viewBox="0 0 17 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.52713 13.7223C10.5582 13.7223 13.3101 9.55365 13.3101 5.93898C13.3101 5.82032 13.3101 5.70232 13.3021 5.58565C13.8375 5.198 14.2996 4.71806 14.6667 4.16832C14.1675 4.38965 13.6378 4.53483 13.0955 4.59898C13.6665 4.25702 14.0939 3.71922 14.2981 3.08565C13.7612 3.40424 13.1739 3.6288 12.5615 3.74965C12.1491 3.31083 11.6035 3.02021 11.0093 2.92279C10.4151 2.82537 9.80528 2.92658 9.27438 3.21076C8.74347 3.49494 8.32106 3.94623 8.07255 4.49476C7.82403 5.04328 7.76328 5.65844 7.89969 6.24498C6.81213 6.19048 5.74819 5.90787 4.77693 5.4155C3.80567 4.92313 2.9488 4.232 2.26194 3.38698C1.91217 3.98912 1.80502 4.70193 1.96231 5.38029C2.1196 6.05866 2.52951 6.65157 3.10857 7.03832C2.67343 7.02572 2.2477 6.90865 1.86729 6.69698V6.73165C1.86755 7.36318 2.08621 7.97519 2.4862 8.46389C2.88618 8.95259 3.44287 9.28791 4.06186 9.41298C3.65909 9.52279 3.2365 9.53876 2.82658 9.45965C3.00135 10.0033 3.34168 10.4787 3.79996 10.8194C4.25824 11.1601 4.81154 11.349 5.38247 11.3597C4.81524 11.8054 4.16572 12.1349 3.47104 12.3293C2.77636 12.5238 2.05017 12.5794 1.33398 12.493C2.585 13.2959 4.04066 13.7217 5.52713 13.7197"
|
||||
fill="var(--colors-scale9)"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
as="a"
|
||||
// @ts-ignore
|
||||
href="https://discord.supabase.com/"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<svg
|
||||
width="17"
|
||||
height="17"
|
||||
viewBox="0 0 17 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_1182_99731)">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M13.8781 3.13594C12.8583 2.66802 11.7648 2.32328 10.6215 2.12583C10.6006 2.12202 10.5798 2.13154 10.5691 2.15059C10.4285 2.40073 10.2727 2.72706 10.1636 2.98356C8.93387 2.79945 7.71044 2.79945 6.50592 2.98356C6.39681 2.72136 6.23538 2.40073 6.09411 2.15059C6.08339 2.13218 6.06259 2.12265 6.04176 2.12583C4.89905 2.32265 3.80554 2.66739 2.78517 3.13594C2.77633 3.13975 2.76876 3.14611 2.76374 3.15435C0.689563 6.25326 0.12136 9.276 0.400102 12.2613C0.401363 12.2759 0.409561 12.2898 0.420913 12.2987C1.78939 13.3037 3.115 13.9139 4.41599 14.3183C4.43681 14.3247 4.45887 14.317 4.47212 14.2999C4.77987 13.8796 5.0542 13.4364 5.28941 12.9704C5.3033 12.9431 5.29005 12.9107 5.26167 12.9C4.82654 12.7349 4.4122 12.5336 4.01364 12.3051C3.98212 12.2867 3.97959 12.2416 4.00859 12.22C4.09247 12.1571 4.17636 12.0917 4.25645 12.0257C4.27094 12.0136 4.29113 12.0111 4.30816 12.0187C6.92655 13.2142 9.76126 13.2142 12.3488 12.0187C12.3658 12.0105 12.386 12.013 12.4011 12.0251C12.4812 12.0911 12.5651 12.1571 12.6496 12.22C12.6786 12.2416 12.6767 12.2867 12.6452 12.3051C12.2466 12.5381 11.8323 12.7349 11.3965 12.8993C11.3681 12.9101 11.3555 12.9431 11.3694 12.9704C11.6097 13.4358 11.884 13.879 12.1861 14.2993C12.1987 14.317 12.2214 14.3247 12.2422 14.3183C13.5495 13.9139 14.8751 13.3037 16.2436 12.2987C16.2556 12.2898 16.2631 12.2765 16.2644 12.2619C16.598 8.8106 15.7056 5.81265 13.8989 3.15498C13.8944 3.14611 13.8869 3.13975 13.8781 3.13594ZM5.68043 10.4435C4.89211 10.4435 4.24257 9.71978 4.24257 8.83093C4.24257 7.94207 4.87952 7.21831 5.68043 7.21831C6.48763 7.21831 7.13089 7.94843 7.11827 8.83093C7.11827 9.71978 6.48132 10.4435 5.68043 10.4435ZM10.9967 10.4435C10.2084 10.4435 9.55884 9.71978 9.55884 8.83093C9.55884 7.94207 10.1958 7.21831 10.9967 7.21831C11.8039 7.21831 12.4471 7.94843 12.4345 8.83093C12.4345 9.71978 11.8039 10.4435 10.9967 10.4435Z"
|
||||
fill="var(--colors-scale9)"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1182_99731">
|
||||
<rect
|
||||
width="15.9993"
|
||||
height="16"
|
||||
fill="white"
|
||||
transform="translate(0.333984 0.222656)"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</LayoutMainContent>
|
||||
)
|
||||
|
||||
export default Footer
|
||||
@@ -22,3 +22,14 @@ export interface References {
|
||||
currentVersion?: string
|
||||
}
|
||||
}
|
||||
|
||||
type MenuItem = {
|
||||
label: string
|
||||
icon?: string
|
||||
href?: string
|
||||
level?: string
|
||||
hasLightIcon?: boolean
|
||||
community?: boolean
|
||||
}
|
||||
|
||||
export type HomepageMenuItems = MenuItem[][]
|
||||
@@ -4,155 +4,18 @@ import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Fragment } from 'react'
|
||||
import { Badge } from '~/../../packages/ui'
|
||||
import { cn } from 'ui/src/utils/cn'
|
||||
import { HOMEPAGE_MENU_ITEMS } from './NavigationMenu.constants'
|
||||
import HomeMenuIconPicker from './HomeMenuIconPicker'
|
||||
|
||||
const home = [
|
||||
[
|
||||
{
|
||||
label: 'Home',
|
||||
icon: '/img/icons/menu/home',
|
||||
href: '/',
|
||||
level: 'home',
|
||||
},
|
||||
{
|
||||
label: 'Getting Started',
|
||||
icon: '/img/icons/menu/getting-started',
|
||||
href: '/guides/getting-started',
|
||||
level: 'gettingstarted',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Database',
|
||||
icon: '/img/icons/menu/database',
|
||||
href: '/guides/database',
|
||||
level: 'database',
|
||||
},
|
||||
{
|
||||
label: 'Serverless APIs',
|
||||
icon: '/img/icons/menu/reference',
|
||||
href: '/guides/api',
|
||||
level: 'api',
|
||||
},
|
||||
{
|
||||
label: 'Auth',
|
||||
icon: '/img/icons/menu/auth',
|
||||
href: '/guides/auth',
|
||||
level: 'auth',
|
||||
},
|
||||
{
|
||||
label: 'Edge Functions',
|
||||
icon: '/img/icons/menu/functions',
|
||||
href: '/guides/functions',
|
||||
level: 'functions',
|
||||
},
|
||||
{
|
||||
label: 'Realtime',
|
||||
icon: '/img/icons/menu/realtime',
|
||||
href: '/guides/realtime',
|
||||
level: 'realtime',
|
||||
},
|
||||
{
|
||||
label: 'Storage',
|
||||
icon: '/img/icons/menu/storage',
|
||||
href: '/guides/storage',
|
||||
level: 'storage',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Platform',
|
||||
icon: '/img/icons/menu/platform',
|
||||
href: '/guides/platform',
|
||||
level: 'platform',
|
||||
},
|
||||
{
|
||||
label: 'Resources',
|
||||
icon: '/img/icons/menu/platform',
|
||||
href: '/guides/resources',
|
||||
level: 'resources',
|
||||
},
|
||||
{
|
||||
label: 'Self-Hosting',
|
||||
icon: '/img/icons/menu/platform',
|
||||
href: '/guides/self-hosting',
|
||||
level: 'self_hosting',
|
||||
},
|
||||
{
|
||||
label: 'Integrations',
|
||||
icon: '/img/icons/menu/integrations',
|
||||
hasLightIcon: true,
|
||||
href: '/guides/integrations',
|
||||
level: 'integrations',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Client Library Reference',
|
||||
},
|
||||
{
|
||||
label: 'JavaScript',
|
||||
icon: '/img/icons/menu/reference-javascript',
|
||||
href: '/reference/javascript/introduction',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
{
|
||||
label: 'Flutter',
|
||||
icon: '/img/icons/menu/reference-dart',
|
||||
href: '/reference/dart/introduction',
|
||||
level: 'reference_dart',
|
||||
},
|
||||
{
|
||||
label: 'Python',
|
||||
icon: '/img/icons/menu/reference-python',
|
||||
href: '/reference/python/introduction',
|
||||
level: 'reference_python',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'C#',
|
||||
icon: '/img/icons/menu/reference-csharp',
|
||||
href: '/reference/csharp/introduction',
|
||||
level: 'reference_csharp',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'Swift',
|
||||
icon: '/img/icons/menu/reference-swift',
|
||||
href: '/reference/swift/introduction',
|
||||
level: 'reference_swift',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'Tools',
|
||||
},
|
||||
{
|
||||
label: 'Management API',
|
||||
icon: '/img/icons/menu/reference-api',
|
||||
href: '/reference/api/introduction',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
{
|
||||
label: 'Supabase CLI',
|
||||
icon: '/img/icons/menu/reference-cli',
|
||||
href: '/guides/cli',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
],
|
||||
]
|
||||
|
||||
const NavigationMenuHome = ({ active }) => {
|
||||
const NavigationMenuHome = () => {
|
||||
const router = useRouter()
|
||||
const { isDarkMode } = useTheme()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
'transition-all duration-150 ease-out',
|
||||
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) => {
|
||||
<div className="transition-all duration-150 ease-out opacity-100 ml-0 delay-150">
|
||||
<ul className="relative w-full flex flex-col gap-4 pb-5">
|
||||
{HOMEPAGE_MENU_ITEMS.map((section, sectionIndex) => {
|
||||
return (
|
||||
<Fragment key={`section-container-${sectionIndex}-border`}>
|
||||
{sectionIndex !== 0 && (
|
||||
@@ -162,16 +25,19 @@ const NavigationMenuHome = ({ active }) => {
|
||||
></div>
|
||||
)}
|
||||
<div key={`section-${sectionIndex}`}>
|
||||
<div className="flex flex-col gap-3">
|
||||
{section.map((link) => {
|
||||
<div className="flex flex-col gap-4">
|
||||
{section.map((link, i) => {
|
||||
if (!link.href) {
|
||||
return (
|
||||
<span
|
||||
<div
|
||||
key={link.label}
|
||||
className="font-mono uppercase text-xs text-scale-900"
|
||||
className={cn(
|
||||
'font-mono uppercase text-xs text-scale-900 ',
|
||||
i !== 0 && 'mt-4'
|
||||
)}
|
||||
>
|
||||
{link.label}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
@@ -179,19 +45,11 @@ const NavigationMenuHome = ({ active }) => {
|
||||
<a>
|
||||
<li
|
||||
className={[
|
||||
'group flex items-center gap-3',
|
||||
'text-base transition-all duration-150 text-scale-1200 hover:text-brand-900 hover:cursor-pointer ',
|
||||
'group flex items-center gap-2',
|
||||
'text-sm transition-all duration-150 text-scale-1100 hover:text-scale-1200 hover:cursor-pointer ',
|
||||
].join(' ')}
|
||||
>
|
||||
<Image
|
||||
alt={link.label}
|
||||
src={`${router.basePath}${
|
||||
isDarkMode ? link.icon : `${link.icon}-light`
|
||||
}${!link.icon.includes('png') ? '.svg' : ''}`}
|
||||
width={17}
|
||||
height={17}
|
||||
className="w-4 h-4 group-hover:scale-110 ease-out transition-all"
|
||||
/>
|
||||
{link?.icon && <HomeMenuIconPicker icon={link.icon} />}
|
||||
{link.label}
|
||||
{link.community && <Badge size="small">Community</Badge>}
|
||||
</li>
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
IconMenuApi,
|
||||
IconMenuAuth,
|
||||
IconMenuCli,
|
||||
IconMenuCsharp,
|
||||
IconMenuDatabase,
|
||||
IconMenuEdgeFunctions,
|
||||
IconMenuFlutter,
|
||||
IconMenuGettingStarted,
|
||||
IconMenuHome,
|
||||
IconMenuIntegrations,
|
||||
IconMenuJavascript,
|
||||
IconMenuPlatform,
|
||||
IconMenuPython,
|
||||
IconMenuRealtime,
|
||||
IconMenuResources,
|
||||
IconMenuSelfHosting,
|
||||
IconMenuServerlessApis,
|
||||
IconMenuStorage,
|
||||
IconMenuSwift,
|
||||
IconMenuStatus,
|
||||
IconMenuKotlin,
|
||||
} from './HomeMenuIcons'
|
||||
|
||||
function getMenuIcon(menuKey: string, width: number = 16, height: number = 16) {
|
||||
switch (menuKey) {
|
||||
case 'home':
|
||||
return <IconMenuHome width={width} height={height} />
|
||||
case 'getting-started':
|
||||
return <IconMenuGettingStarted width={width} height={height} />
|
||||
case 'database':
|
||||
return <IconMenuDatabase width={width} height={height} />
|
||||
case 'serverless-apis':
|
||||
return <IconMenuServerlessApis width={width} height={height} />
|
||||
case 'auth':
|
||||
return <IconMenuAuth width={width} height={height} />
|
||||
case 'edge-functions':
|
||||
return <IconMenuEdgeFunctions width={width} height={height} />
|
||||
case 'realtime':
|
||||
return <IconMenuRealtime width={width} height={height} />
|
||||
case 'storage':
|
||||
return <IconMenuStorage width={width} height={height} />
|
||||
case 'platform':
|
||||
return <IconMenuPlatform width={width} height={height} />
|
||||
case 'resources':
|
||||
return <IconMenuResources width={width} height={height} />
|
||||
case 'self-hosting':
|
||||
return <IconMenuSelfHosting width={width} height={height} />
|
||||
case 'integrations':
|
||||
return <IconMenuIntegrations width={width} height={height} />
|
||||
case 'reference-javascript':
|
||||
return <IconMenuJavascript width={width} height={height} />
|
||||
case 'reference-dart':
|
||||
return <IconMenuFlutter width={width} height={height} />
|
||||
case 'reference-python':
|
||||
return <IconMenuPython width={width} height={height} />
|
||||
case 'reference-csharp':
|
||||
return <IconMenuCsharp width={width} height={height} />
|
||||
case 'reference-swift':
|
||||
return <IconMenuSwift width={width} height={height} />
|
||||
case 'reference-kotlin':
|
||||
return <IconMenuKotlin width={width} height={height} />
|
||||
case 'reference-api':
|
||||
return <IconMenuApi width={width} height={height} />
|
||||
case 'reference-cli':
|
||||
return <IconMenuCli width={width} height={height} />
|
||||
case 'status':
|
||||
return <IconMenuStatus width={width} height={height} />
|
||||
default:
|
||||
return <IconMenuPlatform width={width} height={height} />
|
||||
}
|
||||
}
|
||||
|
||||
type HomeMenuIconPickerProps = {
|
||||
icon: string
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
||||
|
||||
export default function HomeMenuIconPicker({
|
||||
icon,
|
||||
width = 16,
|
||||
height = 16,
|
||||
}: HomeMenuIconPickerProps) {
|
||||
return getMenuIcon(icon, width, height)
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
type HomeMenuIcon = {
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
||||
export function IconMenuHome({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.889 13.83V8h4.222v5.83M3 5.494v8.338h10V5.54L7.994 2.02 3 5.493Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuApi({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.7118 15.4651C10.8778 15.8159 9.96154 16.0098 9 16.0098C8.03846 16.0098 7.1222 15.8159 6.28818 15.4651M2.42438 11.4156C2.14983 10.6654 2 9.85506 2 9.00977C2 8.16447 2.14983 7.35417 2.42438 6.60397M6.28818 2.55442C7.1222 2.20364 8.03846 2.00977 9 2.00977C9.96154 2.00977 10.8778 2.20364 11.7118 2.55442M15.4553 6.29795C15.8061 7.13197 16 8.04823 16 9.00977C16 9.97131 15.8061 10.8876 15.4553 11.7216M10.7427 10.861L13.0078 13.1261M4.99476 5.11305L7.25997 7.37826M7.14874 10.7525L4.88369 13.0176M12.8967 5.00452L10.6315 7.26974M6.08663 9.00977C6.08663 7.40076 7.39099 6.0964 9 6.0964C10.609 6.0964 11.9134 7.40076 11.9134 9.00977C11.9134 10.6188 10.609 11.9231 9 11.9231C7.39099 11.9231 6.08663 10.6188 6.08663 9.00977ZM3.10575 3.11551C3.58524 2.63602 4.36265 2.63602 4.84214 3.11551C5.32164 3.59501 5.32164 4.37242 4.84214 4.85191C4.36265 5.3314 3.58524 5.3314 3.10575 4.85191C2.62625 4.37242 2.62625 3.59501 3.10575 3.11551ZM13.1579 13.1676C13.6373 12.6881 14.4148 12.6881 14.8943 13.1676C15.3737 13.6471 15.3737 14.4245 14.8943 14.904C14.4148 15.3835 13.6373 15.3835 13.1579 14.904C12.6784 14.4245 12.6784 13.6471 13.1579 13.1676ZM14.8948 3.11549C15.3743 3.59498 15.3743 4.37239 14.8948 4.85189C14.4153 5.33138 13.6379 5.33138 13.1584 4.85189C12.6789 4.37239 12.6789 3.59498 13.1584 3.11549C13.6379 2.636 14.4153 2.636 14.8948 3.11549ZM4.84214 13.1676C5.32164 13.6471 5.32164 14.4245 4.84214 14.904C4.36265 15.3835 3.58524 15.3835 3.10575 14.904C2.62625 14.4245 2.62625 13.6471 3.10575 13.1676C3.58524 12.6881 4.36265 12.6881 4.84214 13.1676Z"
|
||||
stroke="#A0A0A0"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuAuth({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3.494 9.975h5m-5 0v2h5v-2m-5 0v-2h5v2M10 5V3a2 2 0 1 0-4 0v2m6.473 0v9h-9V5h9Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuCli({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m5.266 5.552 2.4 2.401-2.4 2.402M8 10.149h3M2 2h12v12H2V2Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuCsharp({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="m2.242 11.3.004-.003a1.191 1.191 0 0 1-.134-.57V5.27c0-.442.185-.784.57-1.005.444-.259 1.393-.804 2.33-1.343 1.019-.587 2.024-1.165 2.35-1.356.42-.247.826-.254 1.25-.01l2.37 1.364 2.336 1.345c.189.109.33.247.425.414.098.167.145.366.145.587V10.73c-.001.216-.048.407-.138.573a1.089 1.089 0 0 1-.432.428l-1.786 1.028c-.973.56-1.947 1.12-2.92 1.682-.388.228-.767.236-1.155.042a1.65 1.65 0 0 1-.103-.056c-.382-.227-1.702-.986-2.881-1.664-.747-.429-1.437-.826-1.799-1.036a1.137 1.137 0 0 1-.432-.428Zm7.452-2.351a1.95 1.95 0 0 1-1.698.994 1.94 1.94 0 0 1-1.69-.983A1.946 1.946 0 0 1 9.68 7.02l1.7-.98a3.91 3.91 0 1 0-3.385 5.866 3.913 3.913 0 0 0 3.4-1.974l-1.701-.983Zm2.151-1.88h-.388v.316h-.312v.388h.312v.468h-.312v.388h.312v.316h.388v-.316h.472v.316h.388v-.316h.316v-.388h-.316v-.468h.316v-.388h-.316V7.07h-.388v.316h-.472V7.07Zm0 1.172v-.468h.472v.468h-.472Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuDatabase({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3.502 6h8.996v4H3.502V6ZM3 10.002h10v4H3v-4ZM3 2h10v4H3V2Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuServerlessApis({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M1.923 9.927A5.833 5.833 0 0 1 1.14 7m2.877-5.046A5.833 5.833 0 0 1 7 1.14m5.13 3.025c.465.84.73 1.807.73 2.835m-2.883 5.049A5.832 5.832 0 0 1 7 12.859m6.172-10.027a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM4.81 11.148a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm8.362 0a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM4.81 2.832a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuEdgeFunctions({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M1.857 11.36a7 7 0 0 1 9.41-9.551M4.774 14.212a7 7 0 0 0 9.41-9.497m-8.812 7.845a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm9.296-9.13a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM12.5 8a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuExtensions({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.418 2.098v5m-2.5-2.5h5m-11.836-2.5h5v5h-5v-5Zm5.185 9.145a2.685 2.685 0 1 1-5.37 0 2.685 2.685 0 0 1 5.37 0Zm1.651-2.396h5v5h-5v-5Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuFlutter({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="m11.857 2-3.718.004L2.143 8l1.85 1.852 1.626-1.617L11.857 2ZM8.245 7.531c-.052-.002-.107-.005-.14.04l-3.198 3.197 1.836 1.825-.002.002 1.315 1.316a.549.549 0 0 1 .026.025c.035.036.074.074.13.062.607-.002 1.214-.002 1.821-.001l1.822-.001-3.232-3.235 3.23-3.23H8.31a.39.39 0 0 1-.064 0Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuGettingStarted({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m13.429 8-4.681 2.702-4.681 2.703V2.594l4.68 2.703 4.682 2.702Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuIntegrations({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.83 1.453v5.619m-2.809-2.81h5.62M1.338 1.454h5.62v5.619h-5.62V1.453Zm5.827 10.278a3.017 3.017 0 1 1-6.035 0 3.017 3.017 0 0 1 6.035 0ZM9.02 9.038h5.62v5.619H9.02V9.038Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuJavascript({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M2 2h12v12H2V2Zm9.173 10.06c-.556 0-.87-.29-1.112-.685l-.916.533c.33.654 1.007 1.153 2.054 1.153 1.071 0 1.869-.556 1.869-1.572 0-.941-.541-1.36-1.5-1.771l-.281-.12c-.484-.21-.693-.347-.693-.686 0-.273.209-.483.54-.483.323 0 .532.137.725.483l.878-.563c-.371-.654-.887-.903-1.604-.903-1.007 0-1.651.644-1.651 1.49 0 .917.54 1.352 1.354 1.698l.282.121c.514.225.821.362.821.749 0 .322-.299.556-.766.556Zm-4.37-.007c-.387 0-.548-.266-.726-.58l-.917.556c.265.562.788 1.03 1.691 1.03 1 0 1.684-.532 1.684-1.7V7.51H7.407v3.834c0 .564-.233.709-.604.709Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuPlatform({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.556 5.554 13.77 7.14l-5.772 4.04-5.737-4.04 2.199-1.587M3.313 7.888.813 9.72l6.996 4.926 7.039-4.926-2.33-1.682M3.285 4.74 8 8.061l4.742-3.32L8 1.338 3.286 4.74Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuPython({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M6.424 1.627A9.05 9.05 0 0 1 7.923 1.5a9.885 9.885 0 0 1 1.633.127c.851.14 1.568.771 1.568 1.612v2.953a1.57 1.57 0 0 1-1.568 1.576H6.424c-1.063 0-1.959.903-1.959 1.927v1.416H3.387c-.911 0-1.443-.654-1.666-1.572-.3-1.233-.288-1.97 0-3.152.25-1.03 1.047-1.572 1.959-1.572h4.312V4.42H4.856V3.239c0-.895.241-1.38 1.568-1.612Zm.391 1.417a.592.592 0 0 0-.588-.593.59.59 0 0 0 0 1.182.59.59 0 0 0 .588-.59Zm4.7 3.148V4.815h1.177c.912 0 1.342.675 1.568 1.572.313 1.246.327 2.18 0 3.152-.317.944-.657 1.572-1.568 1.572h-4.7v.394h3.132v1.182c0 .896-.778 1.35-1.568 1.577-1.187.34-2.14.288-3.132 0-.829-.242-1.568-.736-1.568-1.576V9.733c0-.85.71-1.576 1.568-1.576h3.132c1.044 0 1.96-.898 1.96-1.966Zm-1.173 6.69a.589.589 0 1 0-1.177 0c0 .328.265.594.589.594a.59.59 0 0 0 .588-.593Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuRealtime({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.362.984v2.35m-1.866.144L1.365 1.282m2.052 3.92H1.052m8.023 9.653L4.557 4.523 15 9.115l-4.748 1.182-1.177 4.558Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuResources({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M13 12V5l-.009-3H3v10.5m0 0A1.5 1.5 0 0 0 4.5 14H13v-1.65L12.991 11H4.5A1.5 1.5 0 0 0 3 12.5ZM5.005 2v4l1.5-1.695L8.005 6V2h-3Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuSelfHosting({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M13.041 14.37v-2.285a3.245 3.245 0 0 0-3.244-3.244M7.317 10H3v4h3.552v-1.916a3.257 3.257 0 0 1 3.245-3.244m0 0a2 2 0 1 0 0-4 2 2 0 0 0 0 4ZM8 6H3V2h10v4h-1.43M3.502 6v4"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuStorage({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M13 7.616V5.6l-3.618-3.6H3v4.03m9.964-.447L9.38 2v3.584h3.584ZM1.974 6v8h12V7.509h-7.59l-1.533-1.51H1.974Z"
|
||||
stroke="currentColor"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuSwift({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M14.364 4.774c.004.128.005.256.006.384l.001.439v5.245c-.002.128-.003.256-.007.384-.007.28-.024.561-.073.837-.05.28-.133.541-.263.796a2.673 2.673 0 0 1-1.17 1.17c-.254.13-.514.211-.795.262-.276.05-.558.066-.837.073-.128.004-.256.005-.384.006l-.408.001H5.565l-.407-.001c-.128 0-.256-.002-.384-.006a5.571 5.571 0 0 1-.837-.073c-.28-.05-.541-.133-.796-.262a2.674 2.674 0 0 1-1.17-1.17 2.824 2.824 0 0 1-.262-.796 5.582 5.582 0 0 1-.073-.837 18.375 18.375 0 0 1-.006-.384l-.001-.404V5.158c.002-.128.003-.256.007-.384.007-.28.024-.561.073-.837.05-.28.133-.541.262-.796a2.673 2.673 0 0 1 1.362-1.258c.194-.08.393-.136.604-.174.207-.037.417-.056.627-.066.07-.003.14-.006.21-.007.128-.004.256-.005.384-.006l.457-.001H10.842l.384.006c.28.008.561.024.837.074.28.05.541.133.796.262a2.671 2.671 0 0 1 1.17 1.17c.13.255.212.515.262.796.05.276.066.558.073.837Zm-2.64 4.72h.002c1.094 1.347.797 2.791.656 2.519-.572-1.114-1.638-.83-2.178-.55-.044.028-.09.053-.136.078l-.008.004a.306.306 0 0 0-.01.006l.002-.002c-1.124.597-2.632.642-4.149-.01A6.673 6.673 0 0 1 2.908 8.97c.345.255.718.48 1.114.665 1.603.75 3.213.697 4.352 0C6.753 8.386 5.4 6.762 4.361 5.446a5.644 5.644 0 0 1-.534-.736C5.07 5.85 7.033 7.277 7.737 7.672c-1.494-1.58-2.812-3.525-2.75-3.462 2.355 2.372 4.527 3.714 4.527 3.714.082.045.143.081.192.113.044-.114.084-.232.118-.355.376-1.374-.047-2.946-1.004-4.243 2.184 1.311 3.474 3.802 2.946 5.91a75.282 75.282 0 0 0-.041.145Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuKotlin({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
width={width}
|
||||
height={height}
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M14 14H2V2H14L8 8L14 14Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function IconMenuStatus({ width = 16, height = 16 }: HomeMenuIcon) {
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.9895 1.13281C4.64498 1.13281 1.12305 4.65474 1.12305 8.99927C1.12305 13.3438 4.64498 16.8657 8.9895 16.8657C13.334 16.8657 16.856 13.3438 16.856 8.99927C16.856 7.13428 16.207 5.42087 15.1225 4.07258L8.35267 10.8424L6.39571 8.88546M9.06632 4.19659C6.42145 4.19659 4.27737 6.34068 4.27737 8.98555C4.27737 11.6304 6.42145 13.7745 9.06632 13.7745C11.7112 13.7745 13.8553 11.6304 13.8553 8.98555C13.8553 7.99916 13.5571 7.08243 13.0459 6.32059"
|
||||
stroke="#A0A0A0"
|
||||
strokeMiterlimit="10"
|
||||
strokeLinejoin="bevel"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,153 @@
|
||||
import { References } from '../Navigation.types'
|
||||
import { References, HomepageMenuItems } from '../Navigation.types'
|
||||
|
||||
export const HOMEPAGE_MENU_ITEMS: HomepageMenuItems = [
|
||||
[
|
||||
{
|
||||
label: 'Home',
|
||||
icon: 'home',
|
||||
href: '/',
|
||||
level: 'home',
|
||||
},
|
||||
{
|
||||
label: 'Getting Started',
|
||||
icon: 'getting-started',
|
||||
href: '/guides/getting-started',
|
||||
level: 'gettingstarted',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Database',
|
||||
icon: 'database',
|
||||
href: '/guides/database',
|
||||
level: 'database',
|
||||
},
|
||||
{
|
||||
label: 'Serverless APIs',
|
||||
icon: 'serverless-apis',
|
||||
href: '/guides/api',
|
||||
level: 'api',
|
||||
},
|
||||
{
|
||||
label: 'Auth',
|
||||
icon: 'auth',
|
||||
href: '/guides/auth',
|
||||
level: 'auth',
|
||||
},
|
||||
{
|
||||
label: 'Edge Functions',
|
||||
icon: 'edge-functions',
|
||||
href: '/guides/functions',
|
||||
level: 'functions',
|
||||
},
|
||||
{
|
||||
label: 'Realtime',
|
||||
icon: 'realtime',
|
||||
href: '/guides/realtime',
|
||||
level: 'realtime',
|
||||
},
|
||||
{
|
||||
label: 'Storage',
|
||||
icon: 'storage',
|
||||
href: '/guides/storage',
|
||||
level: 'storage',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Platform',
|
||||
icon: 'platform',
|
||||
href: '/guides/platform',
|
||||
level: 'platform',
|
||||
},
|
||||
{
|
||||
label: 'Resources',
|
||||
icon: 'resources',
|
||||
href: '/guides/resources',
|
||||
level: 'resources',
|
||||
},
|
||||
{
|
||||
label: 'Self-Hosting',
|
||||
icon: 'self-hosting',
|
||||
href: '/guides/self-hosting',
|
||||
level: 'self_hosting',
|
||||
},
|
||||
{
|
||||
label: 'Integrations',
|
||||
icon: 'integrations',
|
||||
hasLightIcon: true,
|
||||
href: '/guides/integrations',
|
||||
level: 'integrations',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Client Library Reference',
|
||||
},
|
||||
{
|
||||
label: 'JavaScript',
|
||||
icon: 'reference-javascript',
|
||||
href: '/reference/javascript/introduction',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
{
|
||||
label: 'Flutter',
|
||||
icon: 'reference-dart',
|
||||
href: '/reference/dart/introduction',
|
||||
level: 'reference_dart',
|
||||
},
|
||||
{
|
||||
label: 'Python',
|
||||
icon: 'reference-python',
|
||||
href: '/reference/python/introduction',
|
||||
level: 'reference_python',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'C#',
|
||||
icon: 'reference-csharp',
|
||||
href: '/reference/csharp/introduction',
|
||||
level: 'reference_csharp',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'Swift',
|
||||
icon: 'reference-swift',
|
||||
href: '/reference/swift/introduction',
|
||||
level: 'reference_swift',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'Kotlin',
|
||||
icon: 'reference-kotlin',
|
||||
href: '/reference/kotlin/introduction',
|
||||
level: 'reference_kotlin',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'Tools reference',
|
||||
},
|
||||
{
|
||||
label: 'Management API',
|
||||
icon: 'reference-api',
|
||||
href: '/reference/api/introduction',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
{
|
||||
label: 'Supabase CLI',
|
||||
icon: 'reference-cli',
|
||||
href: '/guides/cli',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Status',
|
||||
icon: 'status',
|
||||
href: 'https://status.supabase.com/',
|
||||
},
|
||||
],
|
||||
]
|
||||
|
||||
export const REFERENCES: References = {
|
||||
javascript: {
|
||||
@@ -22,9 +171,15 @@ export const REFERENCES: References = {
|
||||
swift: {
|
||||
name: 'Swift',
|
||||
library: 'supabase-swift',
|
||||
versions: ['v1'],
|
||||
versions: ['v0'],
|
||||
icon: '/docs/img/libraries/swift-icon.svg',
|
||||
},
|
||||
kotlin: {
|
||||
name: 'Kotlin',
|
||||
library: 'supabase-kt',
|
||||
versions: ['v0'],
|
||||
icon: '/docs/img/libraries/kotlin-icon.svg',
|
||||
},
|
||||
cli: {
|
||||
name: 'CLI',
|
||||
library: undefined,
|
||||
@@ -142,6 +297,10 @@ export const cli = {
|
||||
items: [
|
||||
{ name: 'Overview', url: '/guides/cli' },
|
||||
{ name: 'Managing Environments', url: '/guides/cli/managing-environments' },
|
||||
{
|
||||
name: 'Using environment variables in config.toml',
|
||||
url: '/guides/cli/using-environment-variables-in-config',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -295,6 +454,7 @@ export const auth = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: 'Password Reset', url: '/guides/auth/auth-password-reset' },
|
||||
{ name: 'Email Templates', url: '/guides/auth/auth-email-templates' },
|
||||
],
|
||||
},
|
||||
@@ -503,7 +663,7 @@ export const database = {
|
||||
}
|
||||
|
||||
export const api = {
|
||||
icon: 'reference',
|
||||
icon: 'serverless-apis',
|
||||
title: 'Serverless APIs',
|
||||
url: '/guides/api',
|
||||
items: [
|
||||
@@ -541,7 +701,7 @@ export const api = {
|
||||
}
|
||||
|
||||
export const functions = {
|
||||
icon: 'functions',
|
||||
icon: 'edge-functions',
|
||||
title: 'Edge Functions',
|
||||
url: '/guides/functions',
|
||||
items: [
|
||||
@@ -591,6 +751,7 @@ export const functions = {
|
||||
{ name: 'Browserless.io', url: '/guides/functions/examples/screenshots' },
|
||||
{ name: 'OpenAI API', url: '/guides/functions/examples/openai' },
|
||||
{ name: 'Upstash Redis', url: '/guides/functions/examples/upstash-redis' },
|
||||
{ name: 'Type-Safe SQL with Kysely', url: '/guides/functions/kysely-postgres' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -701,6 +862,10 @@ export const supabase_cli = {
|
||||
items: [
|
||||
{ name: 'Overview', url: '/guides/cli' },
|
||||
{ name: 'Managing Environments', url: '/guides/cli/managing-environments' },
|
||||
{
|
||||
name: 'Using environment variables in config.toml',
|
||||
url: '/guides/cli/using-environment-variables-in-config',
|
||||
},
|
||||
{
|
||||
name: 'Reference',
|
||||
url: undefined,
|
||||
@@ -779,6 +944,10 @@ export const platform = {
|
||||
name: 'HTTP and Project Issues',
|
||||
url: '/guides/platform/troubleshooting',
|
||||
},
|
||||
{
|
||||
name: 'High CPU Usage',
|
||||
url: '/guides/platform/exhaust-cpu',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -827,7 +996,7 @@ export const resources = {
|
||||
|
||||
export const self_hosting = {
|
||||
title: 'Self-Hosting',
|
||||
icon: 'resources',
|
||||
icon: 'self-hosting',
|
||||
url: '/guides/self-hosting',
|
||||
items: [
|
||||
{ name: 'Overview', url: '/guides/self-hosting' },
|
||||
@@ -984,6 +1153,13 @@ export const reference = {
|
||||
items: [],
|
||||
icon: '/img/icons/menu/reference-swift',
|
||||
},
|
||||
{
|
||||
name: 'supabase-kt',
|
||||
url: '/reference/kotlin/start',
|
||||
level: 'reference_kotlin',
|
||||
items: [],
|
||||
icon: '/img/icons/menu/reference-kotlin',
|
||||
},
|
||||
// {
|
||||
// name: 'supabase-python',
|
||||
// url: '/reference/python/start',
|
||||
@@ -1053,13 +1229,20 @@ export const reference_python_v2 = {
|
||||
parent: '/reference',
|
||||
}
|
||||
|
||||
export const reference_swift_v1 = {
|
||||
export const reference_swift_v0 = {
|
||||
icon: 'reference-swift',
|
||||
title: 'swift',
|
||||
url: 'guides/reference/swift',
|
||||
parent: '/reference',
|
||||
}
|
||||
|
||||
export const reference_kotlin_v0 = {
|
||||
icon: 'reference-kotlin',
|
||||
title: 'kotlin',
|
||||
url: 'guides/reference/kotlin',
|
||||
parent: '/reference',
|
||||
}
|
||||
|
||||
export const reference_cli = {
|
||||
icon: 'reference-cli',
|
||||
title: 'Supabase CLI',
|
||||
@@ -1176,11 +1359,18 @@ export const references = [
|
||||
},
|
||||
{
|
||||
label: 'supabase-swift',
|
||||
versions: ['v1'],
|
||||
versions: ['v0'],
|
||||
description: 'something about the reference',
|
||||
icon: '/docs/img/icons/swift-icon.svg',
|
||||
url: '/reference/swift/start',
|
||||
},
|
||||
{
|
||||
label: 'supabase-kt',
|
||||
versions: ['v0'],
|
||||
description: 'something about the reference',
|
||||
icon: '/docs/img/icons/kotlin-icon.svg',
|
||||
url: '/reference/kotlin/start',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,185 +1,240 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { memo, useEffect } from 'react'
|
||||
import { menuState, useMenuLevelId } from '~/hooks/useMenuState'
|
||||
import NavigationMenuHome from './HomeMenu'
|
||||
import NavigationMenuGuideList from './NavigationMenuGuideList'
|
||||
import NavigationMenuRefList from './NavigationMenuRefList'
|
||||
// @ts-expect-error
|
||||
import spec_js_v2 from '~/../../spec/supabase_js_v2.yml' assert { type: 'yml' }
|
||||
// @ts-expect-error
|
||||
import spec_js_v1 from '~/../../spec/supabase_js_v1.yml' assert { type: 'yml' }
|
||||
// @ts-expect-error
|
||||
import spec_dart_v1 from '~/../../spec/supabase_dart_v1.yml' assert { type: 'yml' }
|
||||
// @ts-expect-error
|
||||
import spec_dart_v0 from '~/../../spec/supabase_dart_v0.yml' assert { type: 'yml' }
|
||||
// @ts-expect-error
|
||||
import spec_csharp_v0 from '~/../../spec/supabase_csharp_v0.yml' assert { type: 'yml' }
|
||||
// @ts-expect-error
|
||||
import spec_python_v2 from '~/../../spec/supabase_py_v2.yml' assert { type: 'yml' }
|
||||
// @ts-expect-error
|
||||
import spec_swift_v1 from '~/../../spec/supabase_swift_v1.yml' assert { type: 'yml' }
|
||||
|
||||
// import { gen_v3 } from '~/lib/refGenerator/helpers'
|
||||
import apiCommonSections from '~/../../spec/common-api-sections.json'
|
||||
import cliCommonSections from '~/../../spec/common-cli-sections.json'
|
||||
import libCommonSections from '~/../../spec/common-client-libs-sections.json'
|
||||
import authServerCommonSections from '~/../../spec/common-self-hosting-auth-sections.json'
|
||||
import realtimeServerCommonSections from '~/../../spec/common-self-hosting-realtime-sections.json'
|
||||
import analyticsServerCommonSections from '~/../../spec/common-self-hosting-analytics-sections.json'
|
||||
import functionsServerCommonSections from '~/../../spec/common-self-hosting-functions-sections.json'
|
||||
import storageServerCommonSections from '~/../../spec/common-self-hosting-storage-sections.json'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import NavigationMenuHome from './HomeMenu'
|
||||
|
||||
// Filter libCommonSections for just the relevant sections in the current library
|
||||
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
|
||||
interface BaseMenu {
|
||||
id: string
|
||||
path: string
|
||||
type: string
|
||||
}
|
||||
|
||||
export type RefIdOptions =
|
||||
| 'reference_javascript_v1'
|
||||
| 'reference_javascript_v2'
|
||||
| 'reference_dart_v0'
|
||||
| 'reference_dart_v1'
|
||||
| 'reference_csharp_v0'
|
||||
| 'reference_python_v2'
|
||||
| 'reference_swift_v1'
|
||||
| 'reference_cli'
|
||||
| 'reference_api'
|
||||
| 'reference_self_hosting_auth'
|
||||
| 'reference_self_hosting_storage'
|
||||
| 'reference_self_hosting_realtime'
|
||||
| 'reference_self_hosting_analytics'
|
||||
| 'reference_self_hosting_functions'
|
||||
interface HomeMenu extends BaseMenu {
|
||||
type: 'home'
|
||||
}
|
||||
|
||||
export type RefKeyOptions =
|
||||
| 'javascript'
|
||||
| 'dart'
|
||||
| 'csharp'
|
||||
| 'python'
|
||||
| 'swift'
|
||||
| 'cli'
|
||||
| 'api'
|
||||
| 'self-hosting-auth'
|
||||
| 'self-hosting-storage'
|
||||
| 'self-hosting-realtime'
|
||||
| 'self-hosting-analytics'
|
||||
| 'self-hosting-functions'
|
||||
interface GuideMenu extends BaseMenu {
|
||||
type: 'guide'
|
||||
}
|
||||
|
||||
interface ReferenceMenu extends BaseMenu {
|
||||
type: 'reference'
|
||||
commonSectionsFile: string
|
||||
specFile?: string
|
||||
}
|
||||
|
||||
type Menu = HomeMenu | GuideMenu | ReferenceMenu
|
||||
|
||||
const menus: Menu[] = [
|
||||
{
|
||||
id: 'home',
|
||||
path: '/',
|
||||
type: 'home',
|
||||
},
|
||||
{
|
||||
id: 'gettingstarted',
|
||||
path: '/guides/getting-started',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'database',
|
||||
path: '/guides/database',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'api',
|
||||
path: '/guides/api',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'auth',
|
||||
path: '/guides/auth',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'functions',
|
||||
path: '/guides/functions',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'realtime',
|
||||
path: '/guides/realtime',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'storage',
|
||||
path: '/guides/storage',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'platform',
|
||||
path: '/guides/platform',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'resources',
|
||||
path: '/guides/resources',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'self_hosting',
|
||||
path: '/guides/self-hosting',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'integrations',
|
||||
path: '/guides/integrations',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'supabase_cli',
|
||||
// TODO: Add path '/reference/cli/config'
|
||||
path: '/guides/cli',
|
||||
type: 'guide',
|
||||
},
|
||||
{
|
||||
id: 'reference_javascript_v1',
|
||||
path: '/reference/javascript/v1',
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_js_v1.yml',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_javascript_v2',
|
||||
path: '/reference/javascript',
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_js_v2.yml',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_dart_v0',
|
||||
path: '/reference/dart/v0',
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_dart_v0.yml',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_dart_v1',
|
||||
path: '/reference/dart',
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_dart_v1.yml',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_csharp_v0',
|
||||
path: '/reference/csharp',
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_csharp_v0.yml',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_python_v2',
|
||||
path: '/reference/python',
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_py_v2.yml',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_swift_v0',
|
||||
path: '/reference/swift',
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_swift_v0.yml',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_kotlin_v0',
|
||||
path: '/reference/kotlin',
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_kt_v0.yml',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_cli',
|
||||
path: '/reference/cli',
|
||||
commonSectionsFile: 'common-cli-sections.json',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_api',
|
||||
path: '/reference/api',
|
||||
commonSectionsFile: 'common-api-sections.json',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_self_hosting_auth',
|
||||
path: '/reference/self-hosting-auth',
|
||||
commonSectionsFile: 'common-self-hosting-auth-sections.json',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_self_hosting_storage',
|
||||
path: '/reference/self-hosting-storage',
|
||||
commonSectionsFile: 'common-self-hosting-storage-sections.json',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_self_hosting_realtime',
|
||||
path: '/reference/self-hosting-realtime',
|
||||
commonSectionsFile: 'common-self-hosting-realtime-sections.json',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_self_hosting_analytics',
|
||||
path: '/reference/self-hosting-analytics',
|
||||
commonSectionsFile: 'common-self-hosting-analytics-sections.json',
|
||||
type: 'reference',
|
||||
},
|
||||
{
|
||||
id: 'reference_self_hosting_functions',
|
||||
path: '/reference/self-hosting-functions',
|
||||
commonSectionsFile: 'common-self-hosting-functions-sections.json',
|
||||
type: 'reference',
|
||||
},
|
||||
]
|
||||
|
||||
function getMenuById(id: string) {
|
||||
return menus.find((menu) => menu.id === id)
|
||||
}
|
||||
|
||||
function getMenuByUrl(basePath: string, url: string) {
|
||||
// If multiple matches, choose the menu with the longest path
|
||||
const [menu] = menus
|
||||
.filter(({ path }) => url.startsWith(`${basePath}${path}`))
|
||||
.sort((a, b) => b.path.length - a.path.length)
|
||||
|
||||
return menu
|
||||
}
|
||||
|
||||
function getMenuElement(menu: Menu) {
|
||||
const menuType = menu.type
|
||||
switch (menuType) {
|
||||
case 'home':
|
||||
return <NavigationMenuHome />
|
||||
case 'guide':
|
||||
return <NavigationMenuGuideList id={menu.id} />
|
||||
case 'reference':
|
||||
return (
|
||||
<NavigationMenuRefList
|
||||
id={menu.id}
|
||||
basePath={menu.path}
|
||||
commonSectionsFile={menu.commonSectionsFile}
|
||||
specFile={menu.specFile}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
throw new Error(`Unknown menu type '${menuType}'`)
|
||||
}
|
||||
}
|
||||
|
||||
const NavigationMenu = () => {
|
||||
const router = useRouter()
|
||||
|
||||
function handleRouteChange(url: string) {
|
||||
switch (url) {
|
||||
case `/docs`:
|
||||
menuState.setMenuLevelId('home')
|
||||
break
|
||||
case url.includes(`/docs/guides/getting-started`) && url:
|
||||
menuState.setMenuLevelId('gettingstarted')
|
||||
break
|
||||
case url.includes(`/docs/guides/database`) && url:
|
||||
menuState.setMenuLevelId('database')
|
||||
break
|
||||
case url.includes(`/docs/guides/api`) && url:
|
||||
menuState.setMenuLevelId('api')
|
||||
break
|
||||
case url.includes(`/docs/guides/auth`) && url:
|
||||
menuState.setMenuLevelId('auth')
|
||||
break
|
||||
case url.includes(`/docs/guides/functions`) && url:
|
||||
menuState.setMenuLevelId('functions')
|
||||
break
|
||||
case url.includes(`/docs/guides/realtime`) && url:
|
||||
menuState.setMenuLevelId('realtime')
|
||||
break
|
||||
case url.includes(`/docs/guides/storage`) && url:
|
||||
menuState.setMenuLevelId('storage')
|
||||
break
|
||||
case url.includes(`/docs/guides/platform`) && url:
|
||||
menuState.setMenuLevelId('platform')
|
||||
break
|
||||
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
|
||||
case url.includes(`/docs/guides/cli`) && url:
|
||||
menuState.setMenuLevelId('supabase_cli')
|
||||
break
|
||||
// JS v1
|
||||
case url.includes(`/docs/reference/javascript/v1`) && url:
|
||||
menuState.setMenuLevelId('reference_javascript_v1')
|
||||
break
|
||||
// JS v2 (latest)
|
||||
case url.includes(`/docs/reference/javascript`) && url:
|
||||
menuState.setMenuLevelId('reference_javascript_v2')
|
||||
break
|
||||
// dart v0
|
||||
case url.includes(`/docs/reference/dart/v0`) && url:
|
||||
menuState.setMenuLevelId('reference_dart_v0')
|
||||
break
|
||||
// dart v1 (latest)
|
||||
case url.includes(`/docs/reference/dart`) && url:
|
||||
menuState.setMenuLevelId('reference_dart_v1')
|
||||
break
|
||||
// C# v0 (latest)
|
||||
case url.includes(`/docs/reference/csharp`) && url:
|
||||
menuState.setMenuLevelId('reference_csharp_v0')
|
||||
break
|
||||
// python v2 (latest)
|
||||
case url.includes(`/docs/reference/python`) && url:
|
||||
menuState.setMenuLevelId('reference_python_v2')
|
||||
break
|
||||
// swift v1 (latest)
|
||||
case url.includes(`/docs/reference/swift`) && url:
|
||||
menuState.setMenuLevelId('reference_swift_v1')
|
||||
break
|
||||
case url.includes(`/docs/reference/cli/config`) && url:
|
||||
menuState.setMenuLevelId('supabase_cli')
|
||||
break
|
||||
case url.includes(`/docs/reference/cli`) && url:
|
||||
menuState.setMenuLevelId('reference_cli')
|
||||
break
|
||||
case url.includes(`/docs/reference/api`) && url:
|
||||
menuState.setMenuLevelId('reference_api')
|
||||
break
|
||||
case url.includes(`/docs/reference/self-hosting-auth`) && url:
|
||||
menuState.setMenuLevelId('reference_self_hosting_auth')
|
||||
break
|
||||
case url.includes(`/docs/reference/self-hosting-storage`) && url:
|
||||
menuState.setMenuLevelId('reference_self_hosting_storage')
|
||||
break
|
||||
case url.includes(`/docs/reference/self-hosting-realtime`) && url:
|
||||
menuState.setMenuLevelId('reference_self_hosting_realtime')
|
||||
break
|
||||
case url.includes(`/docs/reference/self-hosting-analytics`) && url:
|
||||
menuState.setMenuLevelId('reference_self_hosting_analytics')
|
||||
break
|
||||
case url.includes(`/docs/reference/self-hosting-functions`) && url:
|
||||
menuState.setMenuLevelId('reference_self_hosting_functions')
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
const menu = getMenuByUrl(router.basePath, url)
|
||||
if (menu) {
|
||||
menuState.setMenuLevelId(menu.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,168 +248,9 @@ const NavigationMenu = () => {
|
||||
}, [router.events])
|
||||
|
||||
const level = useMenuLevelId()
|
||||
const menu = getMenuById(level)
|
||||
|
||||
const isHomeActive = 'home' === level
|
||||
const isGettingStartedActive = 'gettingstarted' === level
|
||||
const isDatabaseActive = 'database' === level
|
||||
const isApiActive = 'api' === level
|
||||
const isAuthActive = 'auth' === level
|
||||
const isFunctionsActive = 'functions' === level
|
||||
const isRealtimeActive = 'realtime' === level
|
||||
const isStorageActive = 'storage' === level
|
||||
const issupabase_cliActive = 'supabase_cli' === 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_Csharp_V0 = 'reference_csharp_v0' === level
|
||||
const isReference_Python_V2 = 'reference_python_v2' === level
|
||||
const isReference_Swift_V1 = 'reference_swift_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
|
||||
const isReference_Self_Hosting_Analytics = 'reference_self_hosting_analytics' === level
|
||||
const isReference_Self_Hosting_Functions = 'reference_self_hosting_functions' === level
|
||||
|
||||
return (
|
||||
<div className={['flex relative', 'justify-center lg:justify-start'].join(' ')}>
|
||||
{/* // main menu */}
|
||||
<NavigationMenuHome active={isHomeActive} />
|
||||
<NavigationMenuGuideList id={'gettingstarted'} active={isGettingStartedActive} />
|
||||
<NavigationMenuGuideList id={'database'} active={isDatabaseActive} />
|
||||
<NavigationMenuGuideList id={'api'} active={isApiActive} />
|
||||
<NavigationMenuGuideList id={'auth'} active={isAuthActive} />
|
||||
<NavigationMenuGuideList id={'functions'} active={isFunctionsActive} />
|
||||
<NavigationMenuGuideList
|
||||
id={'realtime'}
|
||||
active={isRealtimeActive}
|
||||
value={['/guides/realtime/extensions']}
|
||||
/>
|
||||
<NavigationMenuGuideList id={'storage'} active={isStorageActive} />
|
||||
<NavigationMenuGuideList id={'supabase_cli'} active={issupabase_cliActive} />
|
||||
<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-v1'}
|
||||
id={'reference_javascript_v1'}
|
||||
active={isReference_Javascript_V1}
|
||||
commonSections={libCommonSections}
|
||||
lib="javascript"
|
||||
spec={spec_js_v1}
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-js-menu'}
|
||||
id={'reference_javascript_v2'}
|
||||
active={isReference_Javascript_V2}
|
||||
commonSections={libCommonSections}
|
||||
lib="javascript"
|
||||
spec={spec_js_v2}
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-dart-menu'}
|
||||
id={'reference_dart_v0'}
|
||||
active={isReference_Dart_V0}
|
||||
commonSections={libCommonSections}
|
||||
lib="dart"
|
||||
spec={spec_dart_v0}
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-dart-menu-v1'}
|
||||
id={'reference_dart_v1'}
|
||||
active={isReference_Dart_V1}
|
||||
commonSections={libCommonSections}
|
||||
lib="dart"
|
||||
spec={spec_dart_v1}
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-csharp-menu-v0'}
|
||||
id={'reference_csharp_v0'}
|
||||
active={isReference_Csharp_V0}
|
||||
commonSections={libCommonSections}
|
||||
lib="csharp"
|
||||
spec={spec_csharp_v0}
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-swift-menu-v1'}
|
||||
id={'reference_swift_v1'}
|
||||
active={isReference_Swift_V1}
|
||||
commonSections={libCommonSections}
|
||||
lib="swift"
|
||||
spec={spec_swift_v1}
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-python-menu-v2'}
|
||||
id={'reference_python_v2'}
|
||||
active={isReference_Python_V2}
|
||||
commonSections={libCommonSections}
|
||||
lib="python"
|
||||
spec={spec_python_v2}
|
||||
/>
|
||||
{/* // 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"
|
||||
/>
|
||||
{/* // Self Hosting Server */}
|
||||
<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-realtime"
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-self-hosting-analytics-menu'}
|
||||
id={'reference_self_hosting_analytics'}
|
||||
active={isReference_Self_Hosting_Analytics}
|
||||
commonSections={analyticsServerCommonSections}
|
||||
lib="self-hosting-analytics"
|
||||
/>
|
||||
<NavigationMenuRefList
|
||||
key={'reference-self-hosting-functions-menu'}
|
||||
id={'reference_self_hosting_functions'}
|
||||
active={isReference_Self_Hosting_Functions}
|
||||
commonSections={functionsServerCommonSections}
|
||||
lib="self-hosting-functions"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
return getMenuElement(menu)
|
||||
}
|
||||
|
||||
export default memo(NavigationMenu)
|
||||
@@ -1,6 +1,84 @@
|
||||
// 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
|
||||
}
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ICommonItem } from '~/components/reference/Reference.types'
|
||||
import { Json } from '~/types'
|
||||
|
||||
/**
|
||||
* Recursively filter common sections and their sub items based on
|
||||
* what is available in their spec
|
||||
*/
|
||||
export function deepFilterSections<T extends ICommonItem>(
|
||||
sections: T[],
|
||||
specFunctionIds: string[]
|
||||
): T[] {
|
||||
return sections
|
||||
.filter(
|
||||
(section) =>
|
||||
section.type === 'category' ||
|
||||
section.type === 'markdown' ||
|
||||
specFunctionIds.includes(section.id)
|
||||
)
|
||||
.map((section) => {
|
||||
if ('items' in section) {
|
||||
return {
|
||||
...section,
|
||||
items: deepFilterSections(section.items, specFunctionIds),
|
||||
}
|
||||
}
|
||||
return section
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports common sections file dynamically.
|
||||
*
|
||||
* Dynamic imports allow for code splitting which
|
||||
* dramatically reduces app bundle size.
|
||||
*
|
||||
* See https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import
|
||||
*/
|
||||
export function useCommonSections(commonSectionsFile: string) {
|
||||
const [commonSections, setCommonSections] = useState<ICommonItem[]>()
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchCommonSections() {
|
||||
const commonSections = await import(
|
||||
/* webpackInclude: /common-.*\.json$/ */
|
||||
/* webpackMode: "lazy" */
|
||||
`~/../../spec/${commonSectionsFile}`
|
||||
)
|
||||
setCommonSections(commonSections.default)
|
||||
}
|
||||
fetchCommonSections()
|
||||
}, [commonSectionsFile])
|
||||
|
||||
return commonSections
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports spec file dynamically.
|
||||
*
|
||||
* Dynamic imports allow for code splitting which
|
||||
* dramatically reduces app bundle size.
|
||||
*
|
||||
* See https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import
|
||||
*/
|
||||
export function useSpec(specFile?: string) {
|
||||
const [spec, setSpec] = useState<Json>()
|
||||
|
||||
useEffect(() => {
|
||||
if (!specFile) {
|
||||
return
|
||||
}
|
||||
async function fetchSpec() {
|
||||
const spec = await import(
|
||||
/* webpackInclude: /supabase_.*\.ya?ml$/ */
|
||||
/* webpackMode: "lazy" */
|
||||
`~/../../spec/${specFile}`
|
||||
)
|
||||
setSpec(spec.default)
|
||||
}
|
||||
fetchSpec()
|
||||
}, [specFile])
|
||||
|
||||
return spec
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import { useRouter } from 'next/router'
|
||||
import { IconChevronLeft } from 'ui'
|
||||
import * as NavItems from './NavigationMenu.constants'
|
||||
|
||||
// @ts-expect-error
|
||||
import clientLibsCommon from '~/../../spec/common-cli.yml' assert { type: 'yml' }
|
||||
|
||||
const NavigationMenuCliList = ({ currentLevel, setLevel, id }) => {
|
||||
@@ -22,7 +21,7 @@ const NavigationMenuCliList = ({ currentLevel, setLevel, id }) => {
|
||||
icon?: string
|
||||
}) => {
|
||||
return (
|
||||
<li key={id} className="function-link-item text-scale-1000">
|
||||
<li key={id} className="function-link-item text-scale-1000 leading-3">
|
||||
<Link href={`#${id}`} passHref>
|
||||
<a className="cursor-pointer transition text-scale-1000 text-sm hover:text-brand-900 flex gap-3">
|
||||
{icon && <img className="w-3" src={`${router.basePath}${icon}`} />}
|
||||
@@ -49,7 +48,7 @@ const NavigationMenuCliList = ({ currentLevel, setLevel, id }) => {
|
||||
}
|
||||
|
||||
const Divider = () => {
|
||||
return <div className="h-px w-full bg-green-500 my-3"></div>
|
||||
return <div className="h-px w-full bg-scale-500 my-3"></div>
|
||||
}
|
||||
|
||||
const MenuSections = [
|
||||
|
||||
@@ -6,11 +6,10 @@ import * as NavItems from './NavigationMenu.constants'
|
||||
|
||||
interface Props {
|
||||
id: string
|
||||
active: boolean
|
||||
collapsible?: boolean
|
||||
value?: string[]
|
||||
}
|
||||
const NavigationMenuGuideList: React.FC<Props> = ({ id, active, value }) => {
|
||||
const NavigationMenuGuideList: React.FC<Props> = ({ id, value }) => {
|
||||
const router = useRouter()
|
||||
|
||||
const menu = NavItems[id]
|
||||
@@ -35,16 +34,7 @@ const NavigationMenuGuideList: React.FC<Props> = ({ id, active, value }) => {
|
||||
key={id}
|
||||
type={value ? 'multiple' : 'single'}
|
||||
value={value ?? firstLevelRoute}
|
||||
className={[
|
||||
'transition-all duration-150 ease-out',
|
||||
// enabled
|
||||
active && 'opacity-100 ml-0 delay-150',
|
||||
// level === 'home' && 'ml-12',
|
||||
|
||||
// disabled
|
||||
// level !== 'home' && level !== id ? '-ml-8' : '',
|
||||
!active ? 'opacity-0 invisible absolute h-0 overflow-hidden' : '',
|
||||
].join(' ')}
|
||||
className="transition-all duration-150 ease-out opacity-100 ml-0 delay-150"
|
||||
>
|
||||
<NavigationMenuGuideListItems menu={menu} id={id} />
|
||||
</Accordion.Root>
|
||||
|
||||
@@ -2,23 +2,10 @@ 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 React, { useEffect, useRef } from 'react'
|
||||
import { IconChevronLeft } from '~/../../packages/ui'
|
||||
import * as Accordion from '@radix-ui/react-accordion'
|
||||
|
||||
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`}
|
||||
/>
|
||||
)
|
||||
})
|
||||
import HomeMenuIconPicker from './HomeMenuIconPicker'
|
||||
|
||||
const HeaderLink = React.memo(function HeaderLink(props: {
|
||||
title: string
|
||||
@@ -43,6 +30,8 @@ const HeaderLink = React.memo(function HeaderLink(props: {
|
||||
const ContentAccordionLink = React.memo(function ContentAccordionLink(props: any) {
|
||||
const router = useRouter()
|
||||
const { isDarkMode } = useTheme()
|
||||
const activeItem = props.subItem.url === router.pathname
|
||||
const activeItemRef = useRef(null)
|
||||
|
||||
const LinkContainer = (props) => {
|
||||
return (
|
||||
@@ -52,26 +41,35 @@ const ContentAccordionLink = React.memo(function ContentAccordionLink(props: any
|
||||
)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// scroll to active item
|
||||
if (activeItem && activeItemRef.current) {
|
||||
// this is a hack, but seems a common one on Stackoverflow
|
||||
setTimeout(() => {
|
||||
activeItemRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
||||
}, 0)
|
||||
}
|
||||
})
|
||||
return (
|
||||
<>
|
||||
{props.subItemIndex === 0 && (
|
||||
<>
|
||||
<div className="h-px w-full bg-green-500 my-3"></div>
|
||||
<div className="h-px w-full bg-scale-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}>
|
||||
<li key={props.subItem.name} ref={activeItem ? activeItemRef : null}>
|
||||
<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',
|
||||
activeItem
|
||||
? 'text-brand-900 font-medium'
|
||||
: 'hover:text-scale-1200 dark:hover:text-scale-1100 text-scale-1000',
|
||||
].join(' ')}
|
||||
parent={props.subItem.parent}
|
||||
>
|
||||
@@ -120,14 +118,14 @@ const ContentLink = React.memo(function ContentLink(props: any) {
|
||||
const router = useRouter()
|
||||
|
||||
return (
|
||||
<li>
|
||||
<Link href={`${props.url}`} passHref>
|
||||
<li className="mb-1.5">
|
||||
<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',
|
||||
: 'hover:text-scale-1200 dark:hover:text-scale-1100 text-scale-1000',
|
||||
].join(' ')}
|
||||
>
|
||||
{props.icon && (
|
||||
@@ -168,8 +166,8 @@ const Content = (props) => {
|
||||
|
||||
<Link href={menu.url ?? ''} passHref>
|
||||
<a>
|
||||
<div className="flex items-center gap-3 my-3">
|
||||
<HeaderImage icon={menu.icon} />
|
||||
<div className="flex items-center gap-3 my-3 text-brand-900">
|
||||
<HomeMenuIconPicker icon={menu.icon} />
|
||||
<HeaderLink title={menu.title} url={menu.url} id={id} />
|
||||
</div>
|
||||
</a>
|
||||
@@ -179,7 +177,7 @@ const Content = (props) => {
|
||||
return (
|
||||
<div key={x.name}>
|
||||
{x.items && x.items.length > 0 ? (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex flex-col gap-2.5">
|
||||
{x.items.map((subItem, subItemIndex) => {
|
||||
return (
|
||||
<ContentAccordionLink
|
||||
|
||||
@@ -1,48 +1,44 @@
|
||||
// import apiCommonSections from '~/../../spec/common-client-libs-sections.json'
|
||||
|
||||
import { RefIdOptions, RefKeyOptions } from './NavigationMenu'
|
||||
import { useCommonSections, useSpec } from './NavigationMenu.utils'
|
||||
import NavigationMenuRefListItems from './NavigationMenuRefListItems'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
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[]
|
||||
active: boolean
|
||||
spec?: any
|
||||
interface NavigationMenuRefListProps {
|
||||
id: string
|
||||
basePath: string
|
||||
commonSectionsFile: string
|
||||
specFile?: string
|
||||
}
|
||||
|
||||
const NavigationMenuRefList: React.FC<INavigationMenuRefList> = ({
|
||||
const NavigationMenuRefList = ({
|
||||
id,
|
||||
lib,
|
||||
commonSections,
|
||||
basePath,
|
||||
commonSectionsFile,
|
||||
specFile,
|
||||
}: NavigationMenuRefListProps) => {
|
||||
const commonSections = useCommonSections(commonSectionsFile)
|
||||
const spec = useSpec(specFile)
|
||||
|
||||
if (!commonSections) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (specFile && !spec) {
|
||||
return null
|
||||
}
|
||||
|
||||
active,
|
||||
spec,
|
||||
}) => {
|
||||
const filteredSections = commonSections.filter((section) => {
|
||||
return !section.excludes?.includes(id)
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
'transition-all duration-150 ease-out',
|
||||
// enabled
|
||||
active && 'opacity-100 ml-0 delay-150 h-auto',
|
||||
// move menu back to margin-left
|
||||
// level === 'home' && 'ml-12',
|
||||
// disabled
|
||||
// level !== 'home' && level !== id ? '-ml-8' : '',
|
||||
!active ? 'opacity-0 invisible absolute h-0 overflow-hidden' : '',
|
||||
].join(' ')}
|
||||
>
|
||||
<NavigationMenuRefListItems id={id} lib={lib} commonSections={filteredSections} spec={spec} />
|
||||
<div className="transition-all duration-150 ease-out opacity-100 ml-0 delay-150 h-auto">
|
||||
<NavigationMenuRefListItems
|
||||
id={id}
|
||||
commonSections={filteredSections}
|
||||
spec={spec}
|
||||
basePath={basePath}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,38 +1,21 @@
|
||||
import * as Accordion from '@radix-ui/react-accordion'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { IconChevronLeft } from 'ui'
|
||||
import { IconChevronLeft, IconChevronUp } 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`}
|
||||
/>
|
||||
)
|
||||
})
|
||||
import { cn } from 'ui/src/utils/cn'
|
||||
import { ICommonItem, ICommonSection } from '~/components/reference/Reference.types'
|
||||
import HomeMenuIconPicker from './HomeMenuIconPicker'
|
||||
import { deepFilterSections } from './NavigationMenu.utils'
|
||||
|
||||
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}
|
||||
@@ -40,106 +23,120 @@ const HeaderLink = React.memo(function HeaderLink(props: any) {
|
||||
)
|
||||
})
|
||||
|
||||
const FunctionLink = React.memo(function FunctionLink({
|
||||
title,
|
||||
id,
|
||||
icon,
|
||||
library,
|
||||
slug,
|
||||
}: {
|
||||
interface FunctionLinkProps {
|
||||
title: string
|
||||
name?: string
|
||||
id: string
|
||||
icon?: string
|
||||
product?: string
|
||||
library: string
|
||||
basePath: string
|
||||
slug: string
|
||||
}) {
|
||||
isParent?: boolean
|
||||
isSubItem?: boolean
|
||||
}
|
||||
|
||||
const FunctionLink = React.memo(function FunctionLink({
|
||||
title,
|
||||
id,
|
||||
icon,
|
||||
basePath,
|
||||
slug,
|
||||
isParent = false,
|
||||
isSubItem = false,
|
||||
}: FunctionLinkProps) {
|
||||
const router = useRouter()
|
||||
const activeAccordianItem = useMenuActiveRefId()
|
||||
const activeAccordionItem = useMenuActiveRefId()
|
||||
|
||||
// check if we're on a versioned page
|
||||
let version = ''
|
||||
if (router.asPath.includes('v1')) {
|
||||
version = 'v1'
|
||||
}
|
||||
const url = `${router.basePath}${basePath}/${slug}`
|
||||
const active = activeAccordionItem === id
|
||||
|
||||
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 className="function-link-item leading-5">
|
||||
<a
|
||||
href={url}
|
||||
/**
|
||||
* We don't actually want to navigate or re-render anything
|
||||
* since ref links are all sub-sections on the same page
|
||||
*/
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
history.pushState({}, '', url)
|
||||
document.getElementById(slug)?.scrollIntoView()
|
||||
}}
|
||||
className={cn(
|
||||
'cursor-pointer transition text-sm hover:text-scale-1200 gap-3 relative',
|
||||
isParent ? 'flex justify-between' : 'leading-3',
|
||||
active ? 'text-brand-900' : 'text-scale-1000'
|
||||
)}
|
||||
>
|
||||
{icon && <Image width={16} height={16} alt={icon} src={`${router.basePath}${icon}`} />}
|
||||
{title}
|
||||
{active && !isSubItem && (
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="absolute -left-[13px] top-0 bottom-0 w-[1px] bg-brand-1000"
|
||||
></div>
|
||||
)}
|
||||
{isParent && (
|
||||
<IconChevronUp
|
||||
width={16}
|
||||
className="data-open-parent:rotate-0 data-closed-parent:rotate-90 transition"
|
||||
/>
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
|
||||
const RenderLink = React.memo(function RenderLink(props: any) {
|
||||
const activeAccordianItem = useMenuActiveRefId()
|
||||
let active = false
|
||||
export interface RenderLinkProps {
|
||||
section: ICommonSection
|
||||
basePath: string
|
||||
}
|
||||
|
||||
const isFilter = props.filterIds?.includes(activeAccordianItem)
|
||||
const isModifier = props.modifierIds?.includes(activeAccordianItem)
|
||||
const isAuthServer = props.authServerIds?.includes(activeAccordianItem)
|
||||
const RenderLink = React.memo(function RenderLink({ section, basePath }: RenderLinkProps) {
|
||||
const activeAccordionItem = useMenuActiveRefId()
|
||||
|
||||
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
|
||||
if (!('items' in section)) {
|
||||
return (
|
||||
<FunctionLink
|
||||
title={section.title}
|
||||
id={section.id}
|
||||
slug={section.slug}
|
||||
basePath={basePath}
|
||||
isParent={false}
|
||||
isSubItem
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
let active =
|
||||
section.id === activeAccordionItem ||
|
||||
section.items.some((item) => item.id === activeAccordionItem)
|
||||
|
||||
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.Root collapsible type="single" value={active ? section.id : ''}>
|
||||
<Accordion.Item value={section.id}>
|
||||
<FunctionLink
|
||||
title={section.title}
|
||||
id={section.id}
|
||||
slug={section.slug}
|
||||
basePath={basePath}
|
||||
isParent
|
||||
isSubItem
|
||||
/>
|
||||
<Accordion.Content className="transition data-open:animate-slide-down data-closed:animate-slide-up border-l border-scale-600 pl-3 ml-1 data-open:mt-2 grid gap-2.5">
|
||||
{section.items.map((item) => {
|
||||
return (
|
||||
<FunctionLink
|
||||
key={item.id}
|
||||
title={item.title}
|
||||
id={item.id}
|
||||
slug={item.slug}
|
||||
basePath={basePath}
|
||||
isParent={false}
|
||||
isSubItem={false}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Accordion.Content>
|
||||
</Accordion.Item>
|
||||
</Accordion.Root>
|
||||
@@ -158,67 +155,25 @@ 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[]
|
||||
interface NavigationMenuRefListItemsProps {
|
||||
id: string
|
||||
basePath: string
|
||||
commonSections: ICommonItem[]
|
||||
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 NavigationMenuRefListItems = ({
|
||||
id,
|
||||
basePath,
|
||||
commonSections,
|
||||
spec,
|
||||
}: NavigationMenuRefListItemsProps) => {
|
||||
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)
|
||||
: []
|
||||
const specFunctionIds = spec?.functions.map(({ id }) => id)
|
||||
const filteredSections = spec
|
||||
? deepFilterSections(commonSections, specFunctionIds)
|
||||
: commonSections
|
||||
|
||||
return (
|
||||
<div className={'w-full flex flex-col gap-0 sticky top-8'}>
|
||||
@@ -237,61 +192,26 @@ const Content: React.FC<INavigationMenuRefList> = ({ id, lib, commonSections, sp
|
||||
<span>Back to Main Menu</span>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<div className="flex items-center gap-3 my-3">
|
||||
<HeaderImage icon={menu.icon} />
|
||||
<HomeMenuIconPicker icon={menu.icon} width={21} height={21} />
|
||||
<HeaderLink title={menu.title} url={menu.url} id={id} />
|
||||
<RevVersionDropdown />
|
||||
</div>
|
||||
|
||||
<ul className="function-link-list flex flex-col gap-1">
|
||||
{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.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}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
<ul className="function-link-list flex flex-col gap-2">
|
||||
{filteredSections.map((section) => {
|
||||
return (
|
||||
<Fragment key={section.title}>
|
||||
{section.type === 'category' ? (
|
||||
<>
|
||||
<Divider />
|
||||
<SideMenuTitle title={section.title} />
|
||||
{section.items.map((item) => (
|
||||
<RenderLink key={item.id} section={item} basePath={basePath} />
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<RenderLink section={section} basePath={basePath} />
|
||||
)}
|
||||
</Fragment>
|
||||
)
|
||||
})}
|
||||
@@ -300,4 +220,4 @@ const Content: React.FC<INavigationMenuRefList> = ({ id, lib, commonSections, sp
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(Content)
|
||||
export default React.memo(NavigationMenuRefListItems)
|
||||
@@ -56,7 +56,7 @@ const TopNavBar: FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="h-[60px] border-b px-4 backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300">
|
||||
<nav className="h-[60px] border-b px-4 backdrop-blur backdrop-filter bg-white-1200 bg-scale-200/90">
|
||||
<div className="max-w-[1400px] grid grid-cols-12 mx-auto gap-4 px-5 h-full">
|
||||
<div className="col-span-3 flex items-center">
|
||||
<button className="mr-4 block stroke-2 lg:hidden" onClick={toggleMobileMenu}>
|
||||
|
||||
@@ -56,7 +56,7 @@ const TopNavBarRef: FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="h-[60px] border-b backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300">
|
||||
<nav className="h-[60px] border-b backdrop-blur backdrop-filter bg-white-1200 dark:bg-scale-200/90">
|
||||
<div className="px-5 max-w-7xl mx-auto flex gap-3 justify-between items-center h-full">
|
||||
<div className={['lg:hidden'].join(' ')}>
|
||||
<Link href="/">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import Link from 'next/link'
|
||||
import { Alert, Button, CodeBlock, GlassPanel, markdownComponents, Tabs } from 'ui'
|
||||
import StepHikeCompact from '~/components/StepHikeCompact'
|
||||
|
||||
// Common components
|
||||
import Admonition from './Admonition'
|
||||
import ButtonCard from './ButtonCard'
|
||||
@@ -31,6 +30,29 @@ import CliGlobalFlagsHandler from '~/components/reference/enrichments/cli/CliGlo
|
||||
import Options from '~/components/Options'
|
||||
import Param from '~/components/Params'
|
||||
|
||||
import {
|
||||
IconMenuJavascript,
|
||||
IconMenuHome,
|
||||
IconMenuGettingStarted,
|
||||
IconMenuDatabase,
|
||||
IconMenuServerlessApis,
|
||||
IconMenuAuth,
|
||||
IconMenuEdgeFunctions,
|
||||
IconMenuRealtime,
|
||||
IconMenuStorage,
|
||||
IconMenuPlatform,
|
||||
IconMenuResources,
|
||||
IconMenuSelfHosting,
|
||||
IconMenuIntegrations,
|
||||
IconMenuFlutter,
|
||||
IconMenuPython,
|
||||
IconMenuCsharp,
|
||||
IconMenuSwift,
|
||||
IconMenuKotlin,
|
||||
IconMenuApi,
|
||||
IconMenuCli,
|
||||
} from './Navigation/NavigationMenu/HomeMenuIcons'
|
||||
|
||||
const components = {
|
||||
...markdownComponents,
|
||||
Admonition,
|
||||
@@ -73,6 +95,26 @@ const components = {
|
||||
CliGlobalFlagsHandler: () => <CliGlobalFlagsHandler />,
|
||||
Options,
|
||||
Param,
|
||||
IconMenuJavascript,
|
||||
IconMenuHome,
|
||||
IconMenuGettingStarted,
|
||||
IconMenuDatabase,
|
||||
IconMenuServerlessApis,
|
||||
IconMenuAuth,
|
||||
IconMenuEdgeFunctions,
|
||||
IconMenuRealtime,
|
||||
IconMenuStorage,
|
||||
IconMenuPlatform,
|
||||
IconMenuResources,
|
||||
IconMenuSelfHosting,
|
||||
IconMenuIntegrations,
|
||||
IconMenuFlutter,
|
||||
IconMenuPython,
|
||||
IconMenuCsharp,
|
||||
IconMenuKotlin,
|
||||
IconMenuSwift,
|
||||
IconMenuApi,
|
||||
IconMenuCli,
|
||||
}
|
||||
|
||||
export default components
|
||||
@@ -1,6 +1,5 @@
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { CodeBlock, IconChevronRight, Tabs } from 'ui'
|
||||
// @ts-expect-error
|
||||
import spec from '~/../../spec/cli_v1_commands.yaml' assert { type: 'yml' }
|
||||
import Options from '~/components/Options'
|
||||
import Param from '~/components/Params'
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useMenuActiveRefId } from '~/hooks/useMenuState'
|
||||
import Admonition from '../Admonition'
|
||||
import { ICommonSection } from './Reference.types'
|
||||
|
||||
export interface OldVersionAlertProps {
|
||||
sections: ICommonSection[]
|
||||
}
|
||||
|
||||
const OldVersionAlert = ({ sections }: OldVersionAlertProps) => {
|
||||
const router = useRouter()
|
||||
const activeRefId = useMenuActiveRefId()
|
||||
|
||||
const activeSection = sections.find(({ id }) => id === activeRefId)
|
||||
|
||||
// Remove the version number from URL to get the latest
|
||||
const latestVersionUrl = router.asPath
|
||||
.split('/')
|
||||
.slice(0, -2)
|
||||
.concat(activeSection ? [activeSection.slug] : [])
|
||||
.join('/')
|
||||
|
||||
return (
|
||||
<div className="sticky top-10 z-10 lg:top-14 lg:w-1/2">
|
||||
<Admonition type="caution">
|
||||
You're viewing an older version of this library.
|
||||
<br />
|
||||
<Link href={latestVersionUrl}>
|
||||
<a className="underline decoration-brand-700 underline-offset-4 decoration-1">
|
||||
Switch to the latest
|
||||
</a>
|
||||
</Link>
|
||||
.
|
||||
</Admonition>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default OldVersionAlert
|
||||
@@ -123,6 +123,8 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
|
||||
? 'dart'
|
||||
: example?.code?.startsWith('```c#')
|
||||
? 'csharp'
|
||||
: example?.code?.startsWith('```kotlin')
|
||||
? 'kotlin'
|
||||
: 'js'
|
||||
// `
|
||||
// import { createClient } from '@supabase/supabase-js'
|
||||
@@ -156,7 +158,8 @@ const RefFunctionSection: React.FC<IRefFunctionSection> = (props) => {
|
||||
.replace('js', '')
|
||||
.replace('ts', '')
|
||||
.replace('dart', '')
|
||||
.replace('c#', ''))}
|
||||
.replace('c#', '')
|
||||
.replace('kotlin', ''))}
|
||||
</CodeBlock>
|
||||
|
||||
{((tables && tables.length > 0) || sql) && (
|
||||
|
||||
@@ -8,28 +8,41 @@ import RefFunctionSection from '~/components/reference/RefFunctionSection'
|
||||
import RefSubLayout from '~/layouts/ref/RefSubLayout'
|
||||
import ApiOperationSection from './ApiOperationSection'
|
||||
import CliCommandSection from './CLICommandSection'
|
||||
import { IAPISpec, ICommonFunc, IRefStaticDoc, ISpec, TypeSpec } from './Reference.types'
|
||||
import OldVersionAlert from './OldVersionAlert'
|
||||
import { IAPISpec, ICommonSection, IRefStaticDoc, ISpec, TypeSpec } from './Reference.types'
|
||||
|
||||
interface RefSectionHandlerProps {
|
||||
sections: ICommonFunc[]
|
||||
sections: ICommonSection[]
|
||||
spec?: ISpec | IAPISpec
|
||||
typeSpec?: TypeSpec
|
||||
pageProps: { docs: IRefStaticDoc[] }
|
||||
type: 'client-lib' | 'cli' | 'api'
|
||||
isOldVersion?: boolean
|
||||
}
|
||||
|
||||
const RefSectionHandler = (props: RefSectionHandlerProps) => {
|
||||
const router = useRouter()
|
||||
|
||||
const slug = router.query.slug[0]
|
||||
const [slug] = router.query.slug
|
||||
|
||||
// When user lands on a url like http://supabase.com/docs/reference/javascript/sign-up
|
||||
// find the #sign-up element and scroll to that
|
||||
useEffect(() => {
|
||||
if (document && slug !== 'start') {
|
||||
document.querySelector(`#${slug}`) && document.querySelector(`#${slug}`).scrollIntoView()
|
||||
document.getElementById(slug)?.scrollIntoView()
|
||||
}, [slug])
|
||||
|
||||
useEffect(() => {
|
||||
function handler() {
|
||||
const [slug] = window.location.pathname.split('/').slice(-1)
|
||||
document.getElementById(slug)?.scrollIntoView()
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('popstate', handler)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('popstate', handler)
|
||||
}
|
||||
}, [])
|
||||
|
||||
function getPageTitle() {
|
||||
switch (props.type) {
|
||||
@@ -58,47 +71,50 @@ const RefSectionHandler = (props: RefSectionHandlerProps) => {
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
</Head>
|
||||
{props.isOldVersion && <OldVersionAlert sections={props.sections} />}
|
||||
<RefSubLayout>
|
||||
{props.sections.map((x, i) => {
|
||||
switch (x.type) {
|
||||
{props.sections.map((section, i) => {
|
||||
const sectionType = section.type
|
||||
switch (sectionType) {
|
||||
case 'markdown':
|
||||
const markdownData = props.pageProps.docs.find((doc) => doc.id === x.id)
|
||||
const markdownData = props.pageProps.docs.find((doc) => doc.id === section.id)
|
||||
|
||||
return <RefEducationSection key={x.id + i} item={x} markdownContent={markdownData} />
|
||||
break
|
||||
return (
|
||||
<RefEducationSection
|
||||
key={section.id + i}
|
||||
item={section}
|
||||
markdownContent={markdownData}
|
||||
/>
|
||||
)
|
||||
case 'function':
|
||||
return (
|
||||
<RefFunctionSection
|
||||
key={x.id + i}
|
||||
funcData={x}
|
||||
commonFuncData={x}
|
||||
key={section.id + i}
|
||||
funcData={section}
|
||||
commonFuncData={section}
|
||||
spec={props.spec}
|
||||
typeSpec={props.typeSpec}
|
||||
/>
|
||||
)
|
||||
case 'cli-command':
|
||||
return <CliCommandSection key={x.id + i} funcData={x} commonFuncData={x} />
|
||||
break
|
||||
return (
|
||||
<CliCommandSection
|
||||
key={section.id + i}
|
||||
funcData={section}
|
||||
commonFuncData={section}
|
||||
/>
|
||||
)
|
||||
case 'operation':
|
||||
return (
|
||||
<ApiOperationSection
|
||||
key={x.id + i}
|
||||
funcData={x}
|
||||
commonFuncData={x}
|
||||
key={section.id + i}
|
||||
funcData={section}
|
||||
commonFuncData={section}
|
||||
spec={props.spec}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<RefFunctionSection
|
||||
key={x.id + i}
|
||||
funcData={x}
|
||||
commonFuncData={x}
|
||||
spec={props.spec}
|
||||
typeSpec={props.typeSpec}
|
||||
/>
|
||||
)
|
||||
break
|
||||
throw new Error(`Unknown common section type '${sectionType}'`)
|
||||
}
|
||||
})}
|
||||
</RefSubLayout>
|
||||
|
||||
@@ -33,19 +33,61 @@ export interface IFunctionDefinition {
|
||||
examples?: []
|
||||
}
|
||||
|
||||
export interface ICommonFunc {
|
||||
id?: string
|
||||
export interface ICommonBase {
|
||||
type: string
|
||||
title: string
|
||||
slug?: string
|
||||
product?: string
|
||||
type?: string
|
||||
parent?: string
|
||||
items?: ICommonFunc[]
|
||||
summary?: string
|
||||
}
|
||||
|
||||
export interface ICommonBaseSection extends ICommonBase {
|
||||
id: string
|
||||
slug: string
|
||||
excludes?: string[]
|
||||
}
|
||||
|
||||
export interface ICommonCategory extends ICommonBase {
|
||||
type: 'category'
|
||||
items: ICommonSection[]
|
||||
excludes?: string[]
|
||||
}
|
||||
|
||||
export interface ICommonMarkdown extends ICommonBaseSection {
|
||||
type: 'markdown'
|
||||
}
|
||||
|
||||
export interface ICommonFunctionGroup extends ICommonBaseSection {
|
||||
type: 'function'
|
||||
isFunc: false
|
||||
product: string
|
||||
items: ICommonFunction[]
|
||||
}
|
||||
|
||||
export interface ICommonFunction extends ICommonBaseSection {
|
||||
type: 'function'
|
||||
product: string
|
||||
parent?: string
|
||||
}
|
||||
|
||||
export interface ICommonCliCommand extends ICommonBaseSection {
|
||||
type: 'cli-command'
|
||||
}
|
||||
|
||||
export interface ICommonApiOperation extends ICommonBaseSection {
|
||||
type: 'operation'
|
||||
}
|
||||
|
||||
export type ICommonSection =
|
||||
| ICommonMarkdown
|
||||
| ICommonFunctionGroup
|
||||
| ICommonFunction
|
||||
| ICommonCliCommand
|
||||
| ICommonApiOperation
|
||||
|
||||
export type ICommonItem = ICommonCategory | ICommonSection
|
||||
|
||||
export interface IRefFunctionSection {
|
||||
funcData: any
|
||||
commonFuncData: ICommonFunc
|
||||
commonFuncData: ICommonFunction
|
||||
spec: any
|
||||
typeSpec?: TypeSpec
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import RefSubLayout from '~/layouts/ref/RefSubLayout'
|
||||
|
||||
// @ts-expect-error
|
||||
import spec from '~/../../spec/cli_v1_commands.yaml' assert { type: 'yaml' }
|
||||
import Param from '~/components/Params'
|
||||
import Options from '~/components/Options'
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
[
|
||||
{ "title": "Contributing", "url": "/handbook/contributing" },
|
||||
{ "title": "Changelog", "url": "https://supabase.com/changelog" },
|
||||
{
|
||||
"title": "Author Styleguide",
|
||||
"url": "https://github.com/supabase/supabase/blob/master/DEVELOPERS.md"
|
||||
},
|
||||
{ "title": "Open Source", "url": "https://supabase.com/docs/oss" },
|
||||
{ "title": "SupaSquad", "url": "https://supabase.com/supasquad" }
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
import { IconCheckCircle, IconLifeBuoy } from '~/../../packages/ui'
|
||||
|
||||
export const primaryLinks = [
|
||||
{
|
||||
featherIcon: IconLifeBuoy,
|
||||
text: 'Need some help?',
|
||||
ctaLabel: 'Contact support',
|
||||
url: 'https://app.supabase.com/support/new',
|
||||
},
|
||||
{
|
||||
icon: 'M12.9521 10.5073C12.766 10.3212 12.5289 10.1943 12.2708 10.1427L10.6794 9.82467C9.80719 9.65024 8.90169 9.77152 8.10609 10.1693L7.89409 10.2747C7.0985 10.6725 6.193 10.7938 5.32076 10.6193L4.03343 10.362C3.81823 10.319 3.59574 10.3298 3.38571 10.3934C3.17569 10.457 2.9846 10.5715 2.82943 10.7267M5.33343 2.88867H10.6668L10.0001 3.55534V7.00334C10.0002 7.35693 10.1407 7.69602 10.3908 7.94601L13.7241 11.2793C14.5641 12.1193 13.9688 13.5553 12.7808 13.5553H3.21876C2.03076 13.5553 1.43609 12.1193 2.27609 11.2793L5.60943 7.94601C5.85949 7.69602 6.00002 7.35693 6.00009 7.00334V3.55534L5.33343 2.88867Z',
|
||||
text: 'Lastest product updates?',
|
||||
ctaLabel: 'See Changelog',
|
||||
url: 'https://supabase.com/changelog',
|
||||
},
|
||||
{
|
||||
featherIcon: IconCheckCircle,
|
||||
text: "Something's not right?",
|
||||
ctaLabel: 'Check system status',
|
||||
url: 'https://status.supabase.com/',
|
||||
},
|
||||
]
|
||||
|
||||
export const secondaryLinks = [
|
||||
{ title: 'Contributing', url: '/handbook/contributing' },
|
||||
{
|
||||
title: 'Author Styleguide',
|
||||
url: 'https://github.com/supabase/supabase/blob/master/DEVELOPERS.md',
|
||||
},
|
||||
{ title: 'Open Source', url: 'https://supabase.com/open-source' },
|
||||
{ title: 'SupaSquad', url: 'https://supabase.com/supasquad' },
|
||||
]
|
||||
@@ -16,6 +16,8 @@ const nonGeneratedReferencePages = [
|
||||
'docs/reference/csharp',
|
||||
'docs/reference/csharp/installing',
|
||||
'docs/reference/csharp/initializing',
|
||||
'docs/reference/kotlin/installing',
|
||||
'docs/reference/kotlin/initializing',
|
||||
'docs/reference/api',
|
||||
'docs/reference/api/release-notes',
|
||||
'docs/reference/cli',
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"site_admin": false
|
||||
},
|
||||
"html_url": "https://github.com/supabase/realtime",
|
||||
"description": "Listen to your to PostgreSQL database in realtime via websockets. Built with Elixir.",
|
||||
"description": "Listen to your PostgreSQL database in realtime via websockets. Built with Elixir.",
|
||||
"fork": false,
|
||||
"url": "https://api.github.com/repos/supabase/realtime",
|
||||
"forks_url": "https://api.github.com/repos/supabase/realtime/forks",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
title: Global Flags
|
||||
subtitle: 'Supabase CLI supports global flags for every command.'
|
||||
description: 'Supabase CLI supports global flags for every command.'
|
||||
---
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<img src="/docs/img/icons/menu/reference-csharp.svg" className="w-8 h-8 rounded" />
|
||||
<IconMenuCsharp width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">C# Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">@supabase-community/supabase-csharp</h2>
|
||||
|
||||
@@ -5,7 +5,7 @@ hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<img src="/docs/img/icons/menu/reference-dart.svg" className="w-8 h-8 rounded" />
|
||||
<IconMenuFlutter width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">Flutter Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">supabase-flutter</h2>
|
||||
|
||||
@@ -5,7 +5,7 @@ hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<img src="/docs/img/icons/menu/reference-dart.svg" className="w-8 h-8 rounded" />
|
||||
<IconMenuFlutter width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">Flutter Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">supabase-flutter</h2>
|
||||
|
||||
@@ -5,7 +5,7 @@ hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<img src="/docs/img/icons/menu/reference-javascript.svg" className="w-8 h-8 rounded" />
|
||||
<IconMenuJavascript width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">Javascript Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">@supabase/supabase-js</h2>
|
||||
|
||||
@@ -5,7 +5,7 @@ hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<img src="/docs/img/icons/javascript-icon.svg" className="w-8 h-8 rounded" />
|
||||
<IconMenuJavascript width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">Javascript Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">@supabase/supabase-js</h2>
|
||||
|
||||
@@ -89,7 +89,6 @@ The signIn() method has been deprecated in favor of more explicit method signatu
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { user, error } = await supabase
|
||||
.auth
|
||||
@@ -98,7 +97,6 @@ const { user, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const {
|
||||
data: { user },
|
||||
@@ -128,7 +126,6 @@ const {
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -137,7 +134,6 @@ const { error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -165,7 +161,6 @@ const { error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -174,7 +169,6 @@ const { error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -201,7 +195,6 @@ const { error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -210,7 +203,6 @@ const { error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -238,7 +230,6 @@ const { error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { error } = await supabase
|
||||
.auth
|
||||
@@ -248,16 +239,15 @@ const { error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.auth
|
||||
.signInWithOtp({ phone })
|
||||
|
||||
|
||||
// After receiving a SMS with a OTP.
|
||||
const { data, error } = await supabase
|
||||
.auth
|
||||
.verifyOtp({ phone, token })
|
||||
.auth
|
||||
.verifyOtp({ phone, token })
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
@@ -281,7 +271,6 @@ const { data, error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.auth
|
||||
@@ -291,7 +280,6 @@ const { data, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.auth
|
||||
@@ -390,7 +378,6 @@ const { user } = session
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { user, error } = await supabase
|
||||
.auth
|
||||
@@ -435,7 +422,6 @@ const { user, error } = supabase.auth.setAuth(access_token)
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const supabase = createClient(
|
||||
SUPABASE_URL,
|
||||
@@ -486,7 +472,6 @@ Previously, these methods return inserted/updated/deleted rows by default (which
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -495,7 +480,6 @@ const { data, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -524,7 +508,6 @@ const { data, error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -534,7 +517,6 @@ const { data, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -564,7 +546,6 @@ const { data, error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -573,7 +554,6 @@ const { data, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -602,7 +582,6 @@ const { data, error } = await supabase
|
||||
defaultActiveId="1.0x"
|
||||
>
|
||||
<TabPanel id="1.0x" label="Before">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
@@ -612,7 +591,6 @@ const { data, error } = await supabase
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="2.0x" label="After">
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
const { data, error } = await supabase
|
||||
.from('my_table')
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
---
|
||||
id: installing
|
||||
title: 'Installing & Initialization'
|
||||
slug: installing
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml
|
||||
---
|
||||
|
||||
### Add one or more modules to your project
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Examples>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<a href="https://github.com/supabase-community/supabase-kt/releases" style={{ marginRight: '8px' }}>
|
||||
<img src="https://img.shields.io/github/release/supabase-community/supabase-kt?label=stable" alt="latest stable supabase-kt version"/>
|
||||
</a>
|
||||
<a href="https://github.com/supabase-community/supabase-kt/releases">
|
||||
<img src="https://img.shields.io/maven-central/v/io.github.jan-tennert.supabase/supabase-kt?label=experimental" alt="latest supabase-kt version"/>
|
||||
</a>
|
||||
</div>
|
||||
Add dependency to your build file:
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin">
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
implementation("io.github.jan-tennert.supabase:[module]:VERSION")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
implementation 'io.github.jan-tennert.supabase:[module]:VERSION'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>[module]</artifactId>
|
||||
<version>VERSION</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
The available modules are: **gotrue-kt**, **realtime-kt**, **storage-kt**, **functions-kt**, **postgrest-kt** and **apollo-graphql**
|
||||
|
||||
When using multiple modules, you can also use the BOM dependency to ensure that all modules use the same version:
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin">
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
implementation(platform("io.github.jan-tennert.supabase:bom:VERSION"))
|
||||
implementation("io.github.jan-tennert.supabase:postgrest-kt")
|
||||
implementation("io.github.jan-tennert.supabase:realtime-kt")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
implementation platform("io.github.jan-tennert.supabase:bom:VERSION")
|
||||
implementation 'io.github.jan-tennert.supabase:postgrest-kt'
|
||||
implementation 'io.github.jan-tennert.supabase:realtime-kt'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>bom</artifactId>
|
||||
<version>VERSION</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>postgrest-kt</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>realtime-kt</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
|
||||
### Add Ktor Client Engine to each of your Kotlin targets (required)
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
You can find a list of engines [here](https://ktor.io/docs/http-client-engines.html)
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin">
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
implementation("io.ktor:ktor-client-[engine]:KTOR_VERSION")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
implementation 'io.ktor:ktor-client-[engine]:KTOR_VERSION'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.ktor</groupId>
|
||||
<artifactId>ktor-client-[engine]</artifactId>
|
||||
<version>KTOR_VERSION</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
Multiplatform example:
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin">
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
//supabase modules
|
||||
}
|
||||
}
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-client-cio:KTOR_VERSION")
|
||||
}
|
||||
}
|
||||
val androidMain by getting {
|
||||
dependsOn(jvmMain)
|
||||
}
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-client-js:KTOR_VERSION")
|
||||
}
|
||||
}
|
||||
val iosMain by getting {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-client-darwin:KTOR_VERSION")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
|
||||
### Add KotlinX Serialization (required for PostgREST and Realtime serialization)
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
Add the serialization plugin
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin">
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
kotlin("plugin.serialization") version "KOTLIN_VERSION"
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version 'KOTLIN_VERSION'
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<compilerPlugins>
|
||||
<plugin>kotlinx-serialization</plugin>
|
||||
</compilerPlugins>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-serialization</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
Now you can create serializable classes in your project:
|
||||
```kotlin
|
||||
@Serializable
|
||||
data class Message(
|
||||
val id: Int,
|
||||
val content: String,
|
||||
@SerialName("author_id")
|
||||
val authorId: String
|
||||
)
|
||||
```
|
||||
And use them in your Supabase queries:
|
||||
```kotlin
|
||||
val messages = client.postgrest["messages"].select().decodeList<Message>()
|
||||
```
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuKotlin width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">Kotlin Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">@supabase-community/supabase-kt</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl">
|
||||
|
||||
This reference documents every object and method available in Supabase's Kotlin Multiplatform library, [supabase-kt](https://github.com/supabase-community/supabase-kt). You can use supabase-kt to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
Supported Kotlin targets:
|
||||
|
||||
- JVM
|
||||
- Android
|
||||
- iOS (Experimental)
|
||||
- JS (Browser)
|
||||
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl bg-slate-300 px-4 py-2 rounded-md">
|
||||
<p>The Kotlin client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.</p>
|
||||
|
||||
<p>Huge thanks to official maintainer, [jan-tennert](https://github.com/jan-tennert).</p>
|
||||
|
||||
</div>
|
||||
@@ -5,7 +5,7 @@ hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<img src="/docs/img/icons/menu/reference-python.svg" className="w-8 h-8 rounded" />
|
||||
<IconMenuPython width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">Python Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">@supabase-community/supabase-py</h2>
|
||||
|
||||
@@ -14,9 +14,9 @@ hideTitle: true
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
The Supabase Auth Server (GoTrue) is a JSON Web Token (JWT)-based API for managing users and issuing access tokens.
|
||||
The Supabase Auth Server (GoTrue) is a JSON Web Token (JWT)-based API for managing users and issuing access tokens.
|
||||
|
||||
GoTrue is an open-source API written in Golang, that acts as a self-standing API service for handling user registration and authentication for JAM projects. It's based on OAuth2 and JWT and handles user signup, authentication, and custom user data.
|
||||
GoTrue is an open-source API written in Golang, that acts as a self-standing API service for handling user registration and authentication for JAM projects. It's based on OAuth2 and JWT and handles user signup, authentication, and custom user data.
|
||||
|
||||
</RefSubLayout.Details>
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ hideTitle: true
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
An S3 compatible object storage service that integrates with Postgres.
|
||||
An S3 compatible object storage service that integrates with Postgres.
|
||||
|
||||
- Uses Postgres as it's datastore for storing metadata
|
||||
- Authorization rules are written as Postgres Row Level Security policies
|
||||
- Integrates with S3 as the storage backend (with more in the pipeline!)
|
||||
- Extremely lightweight and performant
|
||||
- Uses Postgres as it's datastore for storing metadata
|
||||
- Authorization rules are written as Postgres Row Level Security policies
|
||||
- Integrates with S3 as the storage backend (with more in the pipeline!)
|
||||
- Extremely lightweight and performant
|
||||
|
||||
</RefSubLayout.Details>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<img src="/docs/img/icons/menu/reference-swift.svg" className="w-8 h-8 rounded" />
|
||||
<IconMenuSwift width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">Swift Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">@supabase-community/supabase-swift</h2>
|
||||
@@ -13,17 +13,15 @@ hideTitle: true
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl">
|
||||
This reference documents every object and method available in Supabase's Swift library, [supabase-swift](https://github.com/supabase-community/supabase-swift). You can use supabase-swift to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
This reference documents every object and method available in Supabase's Swift library, [supabase-swift](https://github.com/supabase-community/supabase-swift). You can use supabase-swift to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
We also provide a [supabase](https://pub.dev/packages/supabase) package for non-Swift projects.
|
||||
|
||||
We also provide a [supabase](https://pub.dev/packages/supabase) package for non-Swift projects.
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl bg-slate-300 px-4 py-2 rounded-md">
|
||||
<p>The Swift client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.</p>
|
||||
|
||||
<p>
|
||||
Huge thanks to official maintainer, [Maail](https://github.com/maail).
|
||||
</p>
|
||||
<p>Huge thanks to official maintainer, [Maail](https://github.com/maail).</p>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<img src="/docs/img/icons/menu/reference-csharp.svg" className="w-8 h-8 rounded" />
|
||||
<IconMenuCsharp width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-scale-1200 m-0">C# Client Library</h1>
|
||||
<h2 className="text-base font-mono text-scale-1100">supabase-csharp</h2>
|
||||
|
||||
@@ -4,9 +4,6 @@ title: 'Initializing'
|
||||
slug: initializing
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
## Flutter
|
||||
|
||||
For `supabase-flutter`, you will be using the static `initialize()` method on `Supabase` class.
|
||||
@@ -30,8 +27,9 @@ final supabase = Supabase.instance.client;
|
||||
|
||||
### Call `initialize()` with custom headers
|
||||
|
||||
You can pass `headers` to initialize your Supabase client with customer headers.
|
||||
You can pass `headers` to initialize your Supabase client with customer headers.
|
||||
Here is an example of passing a custom auth header to Supabase client.
|
||||
|
||||
```dart title=main.dart
|
||||
Future<void> main() async {
|
||||
await Supabase.initialize(
|
||||
|
||||
@@ -4,9 +4,6 @@ title: 'Installing'
|
||||
slug: installing
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
## Flutter
|
||||
|
||||
For Flutter project, you can use [supabase_flutter](https://pub.dev/packages/supabase_flutter) package.
|
||||
|
||||
@@ -5,9 +5,6 @@ slug: generating-types
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/spec/supabase_js_v1.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
Supabase will soon release native type generators that dump your database types for various languages. For now, we support TypeScript [through third-party tools](/docs/guides/database/api/generating-types).
|
||||
|
||||
## Usage with TypeScript
|
||||
|
||||
@@ -5,9 +5,6 @@ slug: initializing
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/spec/supabase_js_v1.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
You can initialize a new Supabase client using the `createClient()` method.
|
||||
|
||||
The Supabase client is your entrypoint to the rest of the Supabase functionality
|
||||
@@ -117,7 +114,7 @@ const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key
|
||||
```
|
||||
|
||||
By default the API server points to the `public` schema. You can enable other database schemas within the Dashboard.
|
||||
Go to `Settings > API > Schema` and add the schema which you want to expose to the API. You also need to grant `USAGE` on your new schema with the grants you desire, such as `SELECT, INSERT, UPDATE, DELETE`.
|
||||
Go to `Settings > API > Schema` and add the schema which you want to expose to the API. You also need to grant `USAGE` on your new schema with the grants you desire, such as `SELECT, INSERT, UPDATE, DELETE`.
|
||||
|
||||
Note: each client connection can only access a single schema, so the code above can access the `other_schema` schema but cannot access the `public` schema.
|
||||
|
||||
|
||||
@@ -5,9 +5,6 @@ slug: installing
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/spec/supabase_js_v1.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
All JavaScript libraries are built directly by the Supabase team.
|
||||
|
||||
Other languages are built by the community and supported by Supabase.
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
---
|
||||
id: changing-timezones
|
||||
title: "Changing Timezones"
|
||||
title: 'Changing Timezones'
|
||||
slug: changing-timezones
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Data types.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -19,38 +15,22 @@ Data types.
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
alter database postgres
|
||||
alter database postgres
|
||||
set timezone to 'America/New_York';
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- View a full list of timezones on [Wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Change timezone
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -59,11 +39,10 @@ set timezone to 'America/New_York';
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
alter database postgres
|
||||
alter database postgres
|
||||
set timezone to 'America/New_York';
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -77,7 +56,6 @@ Get a full list of timezones supported by your database. This will return the fo
|
||||
- `utc_offset`: Offset from UTC (positive means east of Greenwich)
|
||||
- `is_dst`: True if currently observing daylight savings
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -87,11 +65,10 @@ Get a full list of timezones supported by your database. This will return the fo
|
||||
|
||||
```sql
|
||||
select name, abbrev, utc_offset, is_dst
|
||||
from pg_timezone_names()
|
||||
from pg_timezone_names()
|
||||
order by name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -108,12 +85,11 @@ Use `ilike` (case insensitive search) to find specific timezones.
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
select *
|
||||
from pg_timezone_names()
|
||||
select *
|
||||
from pg_timezone_names()
|
||||
where name ilike '%york%';
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,16 +1,12 @@
|
||||
---
|
||||
id: columns
|
||||
title: "Columns"
|
||||
title: 'Columns'
|
||||
slug: columns
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Creating columns.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -26,33 +22,14 @@ create table table_name (
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### During table creation
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -68,15 +45,12 @@ create table table_name (
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Create column
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -89,7 +63,6 @@ alter table new_table
|
||||
add new_column text;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,16 +1,12 @@
|
||||
---
|
||||
id: connection-strings
|
||||
title: "Connection Strings"
|
||||
title: 'Connection Strings'
|
||||
slug: connection-strings
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
There are various ways to connect to your database, depending on the configuration of your Postgres instance and the tool which you are connecting with.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -20,42 +16,27 @@ There are various ways to connect to your database, depending on the configurati
|
||||
|
||||
```bash
|
||||
postgres://postgres:postgres@localhost:5432/postgres
|
||||
# or
|
||||
# or
|
||||
postgresql://postgres:postgres@localhost:5432/postgres
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- [Official Documentation](https://www.postgresql.org/docs/current/libpq-connect.html).
|
||||
- Avoid using special characters usernames and passwords. If you use special characters in a connection URL, you'll need to URL encode any special characters.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic connection string
|
||||
|
||||
If you're using a default setup, your postgres connection string will likely be in the format:
|
||||
If you're using a default setup, your postgres connection string will likely be in the format:
|
||||
|
||||
`postgres://{user}:{password}@{host}:{port}/{database_name}`
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -65,11 +46,10 @@ If you're using a default setup, your postgres connection string will likely be
|
||||
|
||||
```bash
|
||||
postgres://postgres:postgres@localhost:5432/postgres
|
||||
# or
|
||||
# or
|
||||
postgresql://postgres:postgres@localhost:5432/postgres
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -89,7 +69,6 @@ See full [documentation](http://jdbc.postgresql.org/documentation/head/connect.h
|
||||
jdbc:postgresql://{host}:{port}/{database_name}
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -109,7 +88,6 @@ See full [documentation](http://npgsql.projects.postgresql.org/docs/manual/UserM
|
||||
Server=host;Port=5432;User Id=username;Password=secret;Database=database_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -129,7 +107,6 @@ See full [documentation](http://php.net/manual/en/book.pgsql.php).
|
||||
host=hostname port=5432 dbname=databasename user=username password=secret
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,40 +1,16 @@
|
||||
---
|
||||
id: database-passwords
|
||||
title: "Database Passwords"
|
||||
title: 'Database Passwords'
|
||||
slug: database-passwords
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Manage the passwords of your database users using any super user.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Password reset
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -43,11 +19,10 @@ Manage the passwords of your database users using any super user.
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
alter user postgres
|
||||
alter user postgres
|
||||
with password 'new_password';
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,40 +1,16 @@
|
||||
---
|
||||
id: database-users
|
||||
title: "Database Users"
|
||||
title: 'Database Users'
|
||||
slug: database-users
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Users and Roles are almost interchangeable.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Create New User
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -47,7 +23,6 @@ create user new_user
|
||||
with password 'hello';
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,15 +1,11 @@
|
||||
---
|
||||
id: index
|
||||
title: "Getting started"
|
||||
title: 'Getting started'
|
||||
slug: getting-started
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
|
||||
PostgreSQL, also known as Postgres, is a free and open-source relational
|
||||
database management system emphasizing extensibility and SQL compliance.
|
||||
It was originally named POSTGRES, referring to its origins as a successor
|
||||
to the Ingres database developed at the University of California, Berkeley.
|
||||
PostgreSQL, also known as Postgres, is a free and open-source relational
|
||||
database management system emphasizing extensibility and SQL compliance.
|
||||
It was originally named POSTGRES, referring to its origins as a successor
|
||||
to the Ingres database developed at the University of California, Berkeley.
|
||||
@@ -1,42 +1,19 @@
|
||||
---
|
||||
id: publications
|
||||
title: "Publications"
|
||||
title: 'Publications'
|
||||
slug: publications
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Publications are a way of grouping changes generated from a table or a group of tables.
|
||||
Publications are a way of grouping changes generated from a table or a group of tables.
|
||||
These changes can then be sent to other systems (usually another Postgres database).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Create a Publication
|
||||
|
||||
This publication will contain all changes to all tables.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -45,19 +22,16 @@ This publication will contain all changes to all tables.
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
create publication publication_name
|
||||
create publication publication_name
|
||||
for all tables;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Create a Publication which listens to individual tables
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -66,19 +40,16 @@ for all tables;
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
create publication publication_name
|
||||
create publication publication_name
|
||||
for table table_one, table_two;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Add tables to an existing publication
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -87,19 +58,16 @@ for table table_one, table_two;
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
alter publication publication_name
|
||||
alter publication publication_name
|
||||
add table table_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Listens to inserts only
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -108,20 +76,17 @@ add table table_name;
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
create publication publication_name
|
||||
create publication publication_name
|
||||
for all tables
|
||||
with (publish = 'insert');
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Listens to updates only
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -130,20 +95,17 @@ with (publish = 'insert');
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
create publication publication_name
|
||||
create publication publication_name
|
||||
for all tables
|
||||
with (publish = 'update');
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Listens to deletions only
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -152,20 +114,17 @@ with (publish = 'update');
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
create publication publication_name
|
||||
create publication publication_name
|
||||
for all tables
|
||||
with (publish = 'delete');
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Remove a Publication
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -177,7 +136,6 @@ with (publish = 'delete');
|
||||
drop publication if exists publication_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -186,7 +144,6 @@ drop publication if exists publication_name;
|
||||
|
||||
If you are planning to re-create a publication, it's best to do it in a transaction to ensure the operation succeeds.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -195,16 +152,15 @@ If you are planning to re-create a publication, it's best to do it in a transact
|
||||
<TabsPanel id="sql" label="sql">
|
||||
|
||||
```sql
|
||||
begin;
|
||||
begin;
|
||||
-- remove the realtime publication
|
||||
drop publication if exists publication_name;
|
||||
drop publication if exists publication_name;
|
||||
|
||||
-- re-create the publication but don't enable it for any tables
|
||||
create publication publication_name;
|
||||
create publication publication_name;
|
||||
commit;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,19 +1,15 @@
|
||||
---
|
||||
id: schemas
|
||||
title: "Schemas"
|
||||
title: 'Schemas'
|
||||
slug: schemas
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
Schemas are like "folders". They help to keep your database organized.
|
||||
|
||||
Schemas are like "folders". They help to keep your database organized.
|
||||
|
||||
Schemas are particularly useful for security. You can set different permissions on each schema.
|
||||
Schemas are particularly useful for security. You can set different permissions on each schema.
|
||||
For example, you might want to use a `public` schema for user-facing data, and an `auth` schema for all logins and secured data.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -25,36 +21,20 @@ For example, you might want to use a `public` schema for user-facing data, and a
|
||||
create schema schema_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Schemas contain [tables](/docs/reference/postgres/tables), columns, triggers, functions, etc.
|
||||
- Postgres comes with a `public` schema set up by default.
|
||||
- It is best practice to use lowercase and underscores when naming schemas. For example: `schema_name`, not `Schema Name`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Creating a schema
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -66,15 +46,12 @@ create schema schema_name;
|
||||
create schema schema_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Removing a schema
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -86,7 +63,6 @@ create schema schema_name;
|
||||
drop schema if exists schema_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
@@ -96,7 +72,6 @@ drop schema if exists schema_name;
|
||||
Although it's not recommended, you can use uppercase and spaces when naming your schema by wrapping the name with double-quotes.
|
||||
As a result, you will always need to use double-quotes when referencing your schema.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -108,7 +83,6 @@ As a result, you will always need to use double-quotes when referencing your sch
|
||||
create schema "Schema Name";
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,19 +1,15 @@
|
||||
---
|
||||
id: tables
|
||||
title: "Tables"
|
||||
title: 'Tables'
|
||||
slug: tables
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/postgres.yml
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabsPanel from '@theme/TabsPanel';
|
||||
|
||||
Tables are similar to excel spreadsheets. They contain columns & rows of data. There are a few key differences from a spreadsheet however:
|
||||
|
||||
- Every column is a strict type of data. When you set up a column, you must define what "data type" it is.
|
||||
- Tables can be joined together through relationships. For example you can have a "users" table, which is joined to a "teams" table.
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -28,7 +24,7 @@ create table table_name (
|
||||
name text
|
||||
);
|
||||
|
||||
# with schema
|
||||
# with schema
|
||||
create table schema_name.table_name (
|
||||
id integer primary key,
|
||||
data jsonb,
|
||||
@@ -36,36 +32,20 @@ create table schema_name.table_name (
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Tables contain columns, rows, triggers, comments,
|
||||
- Tables contain columns, rows, triggers, comments,
|
||||
- It is best practice to use lowercase and underscores when naming tables. For example: `table_name`, not `Table Name`.
|
||||
- Tables belong to [schemas](/docs/reference/postgres/schemas). If you don't explicitly pass the schema, Postgres will assume that you want to create the table in the `public` schema.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Create table
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -80,7 +60,7 @@ create table table_name (
|
||||
name text
|
||||
);
|
||||
|
||||
# with schema
|
||||
# with schema
|
||||
create table schema_name.table_name (
|
||||
id integer primary key,
|
||||
data jsonb,
|
||||
@@ -88,15 +68,12 @@ create table schema_name.table_name (
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Primary keys using multiple columns
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -113,15 +90,12 @@ create table table_name (
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Multiple foreign keys to the same table
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -135,15 +109,12 @@ alter table table_name
|
||||
add constraint constraint_name_2 foreign key (column_2) references foreign_table(id);
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Delete a table
|
||||
|
||||
|
||||
|
||||
<Tabs
|
||||
defaultActiveId="sql"
|
||||
groupId="reference/postgres"
|
||||
@@ -155,7 +126,6 @@ alter table table_name
|
||||
delete table if exists table_name;
|
||||
```
|
||||
|
||||
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as _ from 'lodash'
|
||||
import * as fs from 'fs'
|
||||
import { mapValues, values } from 'lodash'
|
||||
|
||||
export const slugify = (text: string) => {
|
||||
if (!text) return ''
|
||||
@@ -34,8 +34,8 @@ export const writeToDisk = (fileName: string, content: any) => {
|
||||
* Convert Object to Array of values
|
||||
*/
|
||||
export const toArrayWithKey = (obj: object, keyAs: string) =>
|
||||
_.values(
|
||||
_.mapValues(obj, (value: any, key: string) => {
|
||||
values(
|
||||
mapValues(obj, (value: any, key: string) => {
|
||||
value[keyAs] = key
|
||||
return value
|
||||
})
|
||||
|
||||
@@ -8,10 +8,11 @@ const flatCommonLibSections = flattenSections(commonLibSections)
|
||||
|
||||
const clientLibFiles = [
|
||||
{ fileName: 'supabase_js_v2', label: 'javascript', version: 'v2', versionSlug: false },
|
||||
{ fileName: 'supabase_js_v1', label: 'javascript', version: 'v1', versionSlug: true },
|
||||
{ fileName: 'supabase_dart_v1', label: 'dart', version: 'v1', versionSlug: false },
|
||||
{ fileName: 'supabase_dart_v0', label: 'dart', version: 'v0', versionSlug: true },
|
||||
{ fileName: 'supabase_dart_v0', label: 'dart', version: 'v0', versionSlug: true },
|
||||
{ fileName: 'supabase_py_v2', label: 'python', version: 'v2', versionSlug: false },
|
||||
{ fileName: 'supabase_csharp_v0', label: 'csharp', version: 'v0', versionSlug: false },
|
||||
{ fileName: 'supabase_swift_v0', label: 'swift', version: 'v0', versionSlug: false },
|
||||
{ fileName: 'supabase_kt_v0', label: 'kotlin', version: 'v0', versionSlug: false },
|
||||
]
|
||||
|
||||
export function generateReferencePages() {
|
||||
|
||||
@@ -12,6 +12,7 @@ interface Props {
|
||||
description?: string
|
||||
hide_table_of_contents?: boolean
|
||||
video?: string
|
||||
tocVideo?: string
|
||||
canonical?: string
|
||||
}
|
||||
children: any
|
||||
@@ -20,7 +21,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const Layout: FC<Props> = (props: Props) => {
|
||||
const { asPath, basePath } = useRouter()
|
||||
const { asPath } = useRouter()
|
||||
|
||||
const hasTableOfContents =
|
||||
props.toc !== undefined &&
|
||||
@@ -59,26 +60,30 @@ const Layout: FC<Props> = (props: Props) => {
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<div className={['relative transition-all ease-out', 'duration-150 '].join(' ')}>
|
||||
{/* <p className="text-brand-900 tracking-wider">Tutorials</p> */}
|
||||
<article className="prose dark:prose-dar max-w-none">
|
||||
<h1>{props.meta.title}</h1>
|
||||
{/* <div className="max-w-xs w-32 h-[1px] bg-gradient-to-r from-brand-800 to-brand-900 my-16"></div> */}
|
||||
<MDXProvider components={components} children={props.children} />
|
||||
</article>
|
||||
</div>
|
||||
{hasTableOfContents && !props.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 md:block md:col-span-3 px-2',
|
||||
].join(' ')}
|
||||
>
|
||||
<TableOfContents toc={props.toc} video={props.meta.video} />
|
||||
<LayoutMainContent>
|
||||
<div className={['relative transition-all ease-out', 'duration-150 '].join(' ')}>
|
||||
<article className="prose dark:prose-dar max-w-none">
|
||||
<h1>{props.meta.title}</h1>
|
||||
<MDXProvider components={components} children={props.children} />
|
||||
</article>
|
||||
</div>
|
||||
)}
|
||||
{hasTableOfContents && !props.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 md:block md:col-span-3 px-2',
|
||||
].join(' ')}
|
||||
>
|
||||
<TableOfContents toc={props.toc} video={props.meta.video} />
|
||||
</div>
|
||||
)}
|
||||
</LayoutMainContent>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const LayoutMainContent: FC<{ className?: string }> = ({ className, children }) => (
|
||||
<div className={['max-w-7xl px-5 mx-auto py-16', className].join(' ')}>{children}</div>
|
||||
)
|
||||
|
||||
export default Layout
|
||||
@@ -0,0 +1,86 @@
|
||||
import { MDXProvider } from '@mdx-js/react'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FC } from 'react'
|
||||
import components from '~/components'
|
||||
import TableOfContents from '~/components/TableOfContents'
|
||||
import HomePageCover from '../components/HomePageCover'
|
||||
import { LayoutMainContent } from './DefaultLayout'
|
||||
|
||||
interface Props {
|
||||
meta: {
|
||||
title: string
|
||||
description?: string
|
||||
hide_table_of_contents?: boolean
|
||||
video?: string
|
||||
canonical?: string
|
||||
}
|
||||
children: any
|
||||
toc?: any
|
||||
menuItems: any
|
||||
}
|
||||
|
||||
const HomeLayout: FC<Props> = (props: Props) => {
|
||||
const { asPath } = useRouter()
|
||||
|
||||
const hasTableOfContents =
|
||||
props.toc !== undefined &&
|
||||
props.toc.json.filter((item) => item.lvl !== 1 && item.lvl <= 3).length > 0
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{asPath === '/' ? 'Supabase Docs' : `${props.meta?.title} | Supabase Docs`}</title>
|
||||
<meta name="description" content={props.meta?.description} />
|
||||
<meta property="og:image" content={`https://supabase.com/docs/img/supabase-og-image.png`} />
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content={`https://supabase.com/docs/img/supabase-og-image.png`}
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
</Head>
|
||||
<NextSeo
|
||||
canonical={props.meta?.canonical ?? `https://supabase.com/docs${asPath}`}
|
||||
openGraph={{
|
||||
url: `https://supabase.com/docs${asPath}`,
|
||||
type: 'article',
|
||||
videos: props.meta?.video && [
|
||||
{
|
||||
// youtube based video meta
|
||||
url: props.meta?.video,
|
||||
width: 640,
|
||||
height: 385,
|
||||
type: 'application/x-shockwave-flash',
|
||||
},
|
||||
],
|
||||
article: {
|
||||
publishedTime: new Date().toISOString(),
|
||||
modifiedTime: new Date().toISOString(),
|
||||
authors: ['Supabase'],
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<HomePageCover meta={props.meta} />
|
||||
<LayoutMainContent>
|
||||
<div className={['relative transition-all ease-out', 'duration-150 '].join(' ')}>
|
||||
<article className="prose dark:prose-dar max-w-none">
|
||||
<MDXProvider components={components} children={props.children} />
|
||||
</article>
|
||||
</div>
|
||||
{hasTableOfContents && !props.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 md:block md:col-span-3 px-2',
|
||||
].join(' ')}
|
||||
>
|
||||
<TableOfContents toc={props.toc} video={props.meta.video} />
|
||||
</div>
|
||||
)}
|
||||
</LayoutMainContent>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default HomeLayout
|
||||
@@ -4,11 +4,10 @@ import Link from 'next/link'
|
||||
import NavigationMenu from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import TopNavBarRef from '~/components/Navigation/NavigationMenu/TopNavBarRef'
|
||||
|
||||
import { memo, useEffect } from 'react'
|
||||
import Head from 'next/head'
|
||||
import { PropsWithChildren, memo } from 'react'
|
||||
import Footer from '~/components/Navigation/Footer'
|
||||
import { menuState, useMenuLevelId, useMenuMobileOpen } from '~/hooks/useMenuState'
|
||||
import Head from 'next/head'
|
||||
import { Announcement, AnnouncementCountdown } from 'ui'
|
||||
|
||||
const levelsData = {
|
||||
home: {
|
||||
@@ -91,10 +90,14 @@ const levelsData = {
|
||||
icon: '/docs/img/icons/menu/reference-python',
|
||||
name: 'Python Reference v2.0',
|
||||
},
|
||||
reference_swift_v1: {
|
||||
reference_swift_v0: {
|
||||
icon: '/docs/img/icons/menu/reference-swift',
|
||||
name: 'Swift Reference v1.0',
|
||||
},
|
||||
reference_kotlin_v0: {
|
||||
icon: '/docs/img/icons/menu/reference-kotlin',
|
||||
name: 'Kotlin Reference v0.0',
|
||||
},
|
||||
reference_cli: {
|
||||
icon: '/docs/img/icons/menu/reference-cli',
|
||||
name: 'CLI Reference',
|
||||
@@ -198,7 +201,7 @@ const MobileMenuBackdrop = memo(function MobileMenuBackdrop() {
|
||||
'left-0',
|
||||
'right-0',
|
||||
'z-10',
|
||||
'backdrop-blur-sm backdrop-filter bg-white-1200 dark:bg-blackA-600',
|
||||
'backdrop-blur-sm backdrop-filter bg-white-1200 dark:bg-scale-200/90',
|
||||
mobileMenuOpen ? 'absolute h-full w-full top-0 left-0' : 'hidden h-0',
|
||||
// always hide on desktop
|
||||
'lg:hidden',
|
||||
@@ -208,25 +211,6 @@ const MobileMenuBackdrop = memo(function MobileMenuBackdrop() {
|
||||
)
|
||||
})
|
||||
|
||||
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 (
|
||||
@@ -292,7 +276,7 @@ const NavContainer = memo(function NavContainer() {
|
||||
'relative',
|
||||
'w-auto',
|
||||
'border-r overflow-auto h-screen',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-scale-200',
|
||||
'flex flex-col',
|
||||
].join(' ')}
|
||||
>
|
||||
@@ -312,24 +296,32 @@ const NavContainer = memo(function NavContainer() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SideMenu />
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
const SiteLayout = ({ children }) => {
|
||||
const SiteLayout = ({ children }: PropsWithChildren<{}>) => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Supabase Docs</title>
|
||||
</Head>
|
||||
<main>
|
||||
<div>
|
||||
<Announcement>
|
||||
<AnnouncementCountdown />
|
||||
</Announcement>
|
||||
</div>
|
||||
<div className="flex flex-row h-screen">
|
||||
<NavContainer />
|
||||
<Container>
|
||||
@@ -340,14 +332,14 @@ const SiteLayout = ({ children }) => {
|
||||
className={[
|
||||
'sticky transition-all top-0',
|
||||
'z-10',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-blackA-300',
|
||||
'backdrop-blur backdrop-filter bg-white-1200 dark:bg-scale-200',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className={['lg:hidden', 'px-5 ', 'border-b z-10'].join(' ')}>
|
||||
<MobileHeader />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grow px-5 max-w-7xl mx-auto py-16">
|
||||
<div className="grow">
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
@@ -7,19 +7,22 @@ import { FC, useEffect, useRef, useState } from 'react'
|
||||
import { IconExternalLink } from 'ui'
|
||||
import components from '~/components'
|
||||
import { highlightSelectedTocItem } from '~/components/CustomHTMLElements/CustomHTMLElements.utils'
|
||||
import FooterHelpCallout, { FooterHelpCalloutType } from '~/components/FooterHelpCallout'
|
||||
import { FooterHelpCalloutType } from '~/components/FooterHelpCallout'
|
||||
import GuidesTableOfContents from '~/components/GuidesTableOfContents'
|
||||
import useHash from '~/hooks/useHash'
|
||||
import { LayoutMainContent } from '../DefaultLayout'
|
||||
import ExpandableVideo from 'ui/src/components/ExpandableVideo/ExpandableVideo'
|
||||
|
||||
interface Props {
|
||||
meta: {
|
||||
title: string
|
||||
description?: string
|
||||
description?: string // used in meta tags
|
||||
hide_table_of_contents?: boolean
|
||||
breadcrumb?: string
|
||||
subtitle?: string
|
||||
subtitle?: string // used on the page under the H1
|
||||
footerHelpType?: FooterHelpCalloutType
|
||||
video?: string
|
||||
tocVideo?: string
|
||||
canonical?: string
|
||||
}
|
||||
children: any
|
||||
@@ -35,7 +38,6 @@ const Layout: FC<Props> = (props) => {
|
||||
const [tocList, setTocList] = useState([])
|
||||
|
||||
const { asPath } = useRouter()
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const EDIT_BUTTON_EXCLUDE_LIST = ['/404']
|
||||
@@ -63,6 +65,7 @@ const Layout: FC<Props> = (props) => {
|
||||
}, [])
|
||||
|
||||
const hasTableOfContents = tocList.length > 0
|
||||
const tocVideoPreview = `http://img.youtube.com/vi/${props.meta.tocVideo}/0.jpg`
|
||||
|
||||
// page type, ie, Auth, Database, Storage etc
|
||||
const ogPageType = asPath.split('/')[2]
|
||||
@@ -103,69 +106,75 @@ const Layout: FC<Props> = (props) => {
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<div className={['grid grid-cols-12 relative gap-4'].join(' ')}>
|
||||
<div
|
||||
className={[
|
||||
'relative',
|
||||
!props.hideToc ? 'col-span-12 md:col-span-9' : 'col-span-12',
|
||||
'transition-all ease-out',
|
||||
'duration-100',
|
||||
].join(' ')}
|
||||
>
|
||||
{props.meta.breadcrumb && (
|
||||
<p className="text-brand-900 tracking-wider mb-3">{props.meta.breadcrumb}</p>
|
||||
)}
|
||||
<article
|
||||
ref={articleRef}
|
||||
className={`${
|
||||
props.meta?.hide_table_of_contents || !hasTableOfContents ? '' : ''
|
||||
} prose dark:prose-dark max-w-none`}
|
||||
>
|
||||
<h1 className="mb-0">{props.meta.title}</h1>
|
||||
{props.meta.subtitle && (
|
||||
<h2 className="mt-3 text-xl text-scale-1100">{props.meta.subtitle}</h2>
|
||||
)}
|
||||
<div className="max-w-xs w-32 h-[1px] bg-gradient-to-r from-brand-800 to-brand-900 my-8"></div>
|
||||
<MDXProvider components={components}>{props.children}</MDXProvider>
|
||||
|
||||
{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>
|
||||
)}
|
||||
</article>
|
||||
</div>
|
||||
{!props.hideToc && hasTableOfContents && !props.meta?.hide_table_of_contents && (
|
||||
<LayoutMainContent className="pb-0">
|
||||
<div className={['grid grid-cols-12 relative gap-4'].join(' ')}>
|
||||
<div
|
||||
className={[
|
||||
'col-span-3',
|
||||
'border-scale-400 dark:bg-scale-200 table-of-contents-height',
|
||||
'thin-scrollbar overflow-y-auto sticky hidden md:block md:col-span-3 px-2',
|
||||
'relative',
|
||||
!props.hideToc ? 'col-span-12 md:col-span-9' : 'col-span-12',
|
||||
'transition-all ease-out',
|
||||
'duration-100',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="border-l">
|
||||
<span className="block font-mono text-xs uppercase text-scale-1200 px-5 mb-6">
|
||||
On this page
|
||||
</span>
|
||||
<GuidesTableOfContents list={tocList} />
|
||||
</div>
|
||||
{props.meta.breadcrumb && (
|
||||
<p className="text-brand-900 tracking-wider mb-3">{props.meta.breadcrumb}</p>
|
||||
)}
|
||||
<article
|
||||
ref={articleRef}
|
||||
className={`${
|
||||
props.meta?.hide_table_of_contents || !hasTableOfContents ? '' : ''
|
||||
} prose dark:prose-dark max-w-none`}
|
||||
>
|
||||
<h1 className="mb-0">{props.meta.title}</h1>
|
||||
{props.meta.subtitle && (
|
||||
<h2 className="mt-3 text-xl text-scale-1100">{props.meta.subtitle}</h2>
|
||||
)}
|
||||
<div className="w-full h-[1px] bg-scale-500 my-8"></div>
|
||||
<MDXProvider components={components}>{props.children}</MDXProvider>
|
||||
|
||||
{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>
|
||||
)}
|
||||
</article>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<FooterHelpCallout footerHelpType={props.meta?.footerHelpType} title={props.meta?.title} />
|
||||
{!props.hideToc && hasTableOfContents && !props.meta?.hide_table_of_contents && (
|
||||
<div
|
||||
className={[
|
||||
'col-span-3',
|
||||
'border-scale-400 dark:bg-scale-200 table-of-contents-height',
|
||||
'thin-scrollbar overflow-y-auto sticky hidden md:block md:col-span-3 px-2',
|
||||
'transition-all ease-out',
|
||||
'duration-100',
|
||||
].join(' ')}
|
||||
>
|
||||
<div className="border-l">
|
||||
{props.meta?.tocVideo && !!tocVideoPreview && (
|
||||
<div className="relative mb-6 pl-5">
|
||||
<ExpandableVideo imgUrl={tocVideoPreview} videoId={props.meta.tocVideo} />
|
||||
</div>
|
||||
)}
|
||||
<span className="block font-mono text-xs uppercase text-scale-1200 px-5 mb-6">
|
||||
On this page
|
||||
</span>
|
||||
<GuidesTableOfContents list={tocList} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</LayoutMainContent>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,11 @@ interface ISectionDetails {}
|
||||
interface ISectionExamples {}
|
||||
|
||||
const RefSubLayout: FC<RefSubLayoutType> & RefSubLayoutSubComponents = (props) => {
|
||||
return <div className="flex flex-col w-full divide-y">{props.children}</div>
|
||||
return (
|
||||
<div className="flex flex-col w-full divide-y px-5 max-w-7xl mx-auto py-16">
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Section: FC<ISectionContainer> = (props) => {
|
||||
@@ -128,7 +132,7 @@ const StickyHeader: FC<StickyHeader> = ({ icon, ...props }) => {
|
||||
}
|
||||
|
||||
const Details: FC<ISectionDetails> = (props) => {
|
||||
return <div className="relative overflow-hidden w-full">{props.children}</div>
|
||||
return <div className="relative w-full">{props.children}</div>
|
||||
}
|
||||
|
||||
const Examples: FC<ISectionExamples> = (props) => {
|
||||
|
||||
@@ -6,7 +6,13 @@ import SideBar from '~/components/Navigation/SideBar'
|
||||
import TableOfContents from '~/components/TableOfContents'
|
||||
|
||||
interface Props {
|
||||
meta: { title: string; description?: string; hide_table_of_contents?: boolean; video?: string }
|
||||
meta: {
|
||||
title: string
|
||||
description?: string
|
||||
hide_table_of_contents?: boolean
|
||||
video?: string
|
||||
tocVideo?: string
|
||||
}
|
||||
children: any
|
||||
toc?: any
|
||||
menuItems: any
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export const IS_PLATFORM = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true'
|
||||
export const LOCAL_SUPABASE = process.env.NEXT_PUBLIC_LOCAL_SUPABASE === 'true'
|
||||
export const API_URL = process.env.NEXT_PUBLIC_API_URL
|
||||
@@ -1,7 +1,5 @@
|
||||
interface DataProps {
|
||||
referrer?: string
|
||||
title: string
|
||||
route?: string
|
||||
[prop: string]: any
|
||||
}
|
||||
|
||||
export const post = (url: string, data: DataProps, options = {}) => {
|
||||
|
||||
+21
-16
@@ -1,6 +1,4 @@
|
||||
// [Joshen] I think this can be done better, as its mostly used to choose what
|
||||
|
||||
import { ICommonFunc } from '../components/reference/Reference.types'
|
||||
import { ICommonBase, ICommonItem, ICommonSection } from '../components/reference/Reference.types'
|
||||
|
||||
// menus to render in the SideBar.js (Ref Nav.constants.ts)
|
||||
export function getPageType(asPath: string) {
|
||||
@@ -36,18 +34,25 @@ export function getPageType(asPath: string) {
|
||||
return page
|
||||
}
|
||||
|
||||
export function flattenSections(sections: ICommonFunc[]) {
|
||||
let a: ICommonFunc[] = []
|
||||
for (let i = 0; i < sections.length; i++) {
|
||||
if (sections[i].id) {
|
||||
// only push a section that has an id
|
||||
// these are reserved for sidebar subtitles
|
||||
a.push(sections[i])
|
||||
/**
|
||||
* Flattens common sections recursively by their `items`.
|
||||
*
|
||||
* _Note:_ `sections` type set to `ICommonBase[]` instead of
|
||||
* `ICommonItem[]` until TypeScript supports JSON imports as const:
|
||||
* https://github.com/microsoft/TypeScript/issues/32063
|
||||
*/
|
||||
export function flattenSections(sections: ICommonBase[]): ICommonSection[] {
|
||||
return sections.reduce<ICommonSection[]>((acc, section: ICommonItem) => {
|
||||
// Flatten sub-items
|
||||
if ('items' in section) {
|
||||
let newSections = acc
|
||||
|
||||
if (section.type !== 'category') {
|
||||
newSections.push(section)
|
||||
}
|
||||
|
||||
return newSections.concat(flattenSections(section.items))
|
||||
}
|
||||
if (sections[i].items) {
|
||||
// if there are subitems, loop through
|
||||
a = a.concat(flattenSections(sections[i].items))
|
||||
}
|
||||
}
|
||||
return a
|
||||
return acc.concat(section)
|
||||
}, [])
|
||||
}
|
||||
@@ -2,12 +2,9 @@ import fs from 'fs'
|
||||
|
||||
import matter from 'gray-matter'
|
||||
import { serialize } from 'next-mdx-remote/serialize'
|
||||
import { ICommonMarkdown } from '~/components/reference/Reference.types'
|
||||
|
||||
// import { remarkCodeHike } from '@code-hike/mdx'
|
||||
// import codeHikeTheme from '~/codeHikeTheme.js'
|
||||
// import theme from 'shiki/themes/solarized-dark.json'
|
||||
|
||||
async function generateRefMarkdown(sections, slug) {
|
||||
async function generateRefMarkdown(sections: ICommonMarkdown[], slug: string) {
|
||||
let markdownContent = []
|
||||
/**
|
||||
* Read all the markdown files that might have
|
||||
@@ -16,10 +13,8 @@ async function generateRefMarkdown(sections, slug) {
|
||||
* - important notes regarding implementation
|
||||
*/
|
||||
await Promise.all(
|
||||
sections.map(async (x, i) => {
|
||||
if (!x.id) return null
|
||||
|
||||
const pathName = `docs/ref${slug}/${x.id}.mdx`
|
||||
sections.map(async (section) => {
|
||||
const pathName = `docs/ref${slug}/${section.id}.mdx`
|
||||
|
||||
function checkFileExists(x) {
|
||||
if (fs.existsSync(x)) {
|
||||
@@ -37,8 +32,8 @@ async function generateRefMarkdown(sections, slug) {
|
||||
const { data, content } = matter(fileContents)
|
||||
|
||||
markdownContent.push({
|
||||
id: x.id,
|
||||
title: x.title,
|
||||
id: section.id,
|
||||
title: section.title,
|
||||
meta: data,
|
||||
// introPage: introPages.includes(x),
|
||||
content: content
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
// @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' }
|
||||
// @ts-expect-error
|
||||
import specAnalyticsV0 from '~/../../spec/analytics_v0_config.yaml' assert { type: 'yml' }
|
||||
// @ts-expect-error
|
||||
import specFunctionsV0 from '~/../../spec/functions_v0_config.yaml' assert { type: 'yml' }
|
||||
|
||||
function getStorageConfigV0() {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { getAllDocs } from '../docs'
|
||||
|
||||
async function handleRefGetStaticPaths() {
|
||||
let docs = getAllDocs()
|
||||
import { ICommonSection } from '~/components/reference/Reference.types'
|
||||
|
||||
async function handleRefGetStaticPaths(sections: ICommonSection[]) {
|
||||
return {
|
||||
paths: docs.map(() => {
|
||||
paths: sections.map((section) => {
|
||||
return {
|
||||
params: {
|
||||
slug: docs.map((d) => d.slug),
|
||||
slug: [section.slug],
|
||||
},
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { ICommonMarkdown, ICommonSection } from '~/components/reference/Reference.types'
|
||||
import generateRefMarkdown from '~/lib/mdx/generateRefMarkdown'
|
||||
|
||||
async function handleRefStaticProps(sections, librarypath) {
|
||||
let markdownContent = await generateRefMarkdown(sections, librarypath)
|
||||
async function handleRefStaticProps(sections: ICommonSection[], libraryPath: string) {
|
||||
const markdownSections = sections.filter(
|
||||
(section): section is ICommonMarkdown => section.type === 'markdown'
|
||||
)
|
||||
const markdownContent = await generateRefMarkdown(markdownSections, libraryPath)
|
||||
|
||||
return {
|
||||
props: {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { TsDoc } from '../../generator/legacy/definitions'
|
||||
import { values, mapValues } from 'lodash'
|
||||
import { OpenAPIV3 } from 'openapi-types'
|
||||
import { flattenSections } from '../helpers'
|
||||
import { ICommonItem } from '~/components/reference/Reference.types'
|
||||
|
||||
export function extractTsDocNode(nodeToFind: string, definition: any) {
|
||||
const nodePath = nodeToFind.split('.')
|
||||
@@ -294,12 +295,13 @@ export const toArrayWithKey = (obj: object, keyAs: string) =>
|
||||
})
|
||||
)
|
||||
|
||||
export function generateAllowedClientLibKeys(sections, spec) {
|
||||
/**
|
||||
* Get a list of common section IDs that are available in this spec
|
||||
*/
|
||||
export function getAvailableSectionIds(sections: ICommonItem[], spec: any) {
|
||||
// Filter parent sections first
|
||||
|
||||
const specIds = spec.functions.map((func) => {
|
||||
return func.id
|
||||
})
|
||||
const specIds = spec.functions.map(({ id }) => id)
|
||||
|
||||
const newShape = flattenSections(sections).filter((section) => {
|
||||
if (specIds.includes(section.id)) {
|
||||
|
||||
@@ -6,6 +6,12 @@ import rehypeSlug from 'rehype-slug'
|
||||
|
||||
import withTM from 'next-transpile-modules'
|
||||
import withYaml from 'next-plugin-yaml'
|
||||
import configureBundleAnalyzer from '@next/bundle-analyzer'
|
||||
|
||||
const withBundleAnalyzer = configureBundleAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
})
|
||||
|
||||
// import admonitions from 'remark-admonitions'
|
||||
|
||||
// import { remarkCodeHike } from '@code-hike/mdx'
|
||||
@@ -39,7 +45,7 @@ const withMDX = nextMdx({
|
||||
},
|
||||
})
|
||||
|
||||
// /** @type {NextConfig} */
|
||||
/** @type {import('next').NextConfig} nextConfig */
|
||||
const nextConfig = {
|
||||
// Append the default value with md extensions
|
||||
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
|
||||
@@ -54,10 +60,16 @@ const nextConfig = {
|
||||
'user-images.githubusercontent.com',
|
||||
'raw.githubusercontent.com',
|
||||
'weweb-changelog.ghost.io',
|
||||
'img.youtube.com',
|
||||
],
|
||||
},
|
||||
experimental: {
|
||||
mdxRs: true,
|
||||
modularizeImports: {
|
||||
lodash: {
|
||||
transform: 'lodash/{{member}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
@@ -92,9 +104,14 @@ const nextConfig = {
|
||||
},
|
||||
}
|
||||
|
||||
// next.config.js
|
||||
export default () => {
|
||||
// const plugins = [withMDX]/
|
||||
const plugins = [withTM(['ui', 'common', '@supabase/auth-helpers-nextjs']), withMDX, withYaml]
|
||||
const configExport = () => {
|
||||
const plugins = [
|
||||
withTM(['ui', 'common', '@supabase/auth-helpers-nextjs']),
|
||||
withMDX,
|
||||
withYaml,
|
||||
withBundleAnalyzer,
|
||||
]
|
||||
return plugins.reduce((acc, next) => next(acc), nextConfig)
|
||||
}
|
||||
|
||||
export default configExport
|
||||
@@ -6,6 +6,7 @@
|
||||
"dev": "next dev --port 3001",
|
||||
"dev:secrets:pull": "AWS_PROFILE=supabase-dev node internals/getSecrets.js",
|
||||
"build": "npm run generate:all && next build",
|
||||
"build:analyze": "ANALYZE=true next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"build:sitemap": "node ./internals/generate-sitemap.mjs",
|
||||
@@ -46,6 +47,7 @@
|
||||
"@docsearch/react": "^3.3.0",
|
||||
"@mdx-js/loader": "^1.6.22",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"@next/bundle-analyzer": "^13.4.0",
|
||||
"@next/mdx": "^12.0.4",
|
||||
"@octokit/auth-app": "^4.0.9",
|
||||
"@octokit/core": "^4.2.0",
|
||||
@@ -59,6 +61,7 @@
|
||||
"clsx": "^1.2.1",
|
||||
"common": "*",
|
||||
"config": "*",
|
||||
"framer-motion": "^6.5.1",
|
||||
"github-slugger": "^2.0.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"isbot": "^3.6.5",
|
||||
|
||||
+41
-22
@@ -1,16 +1,13 @@
|
||||
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { SessionContextProvider } from '@supabase/auth-helpers-react'
|
||||
import { AuthProvider, ThemeProvider } from 'common'
|
||||
import { AuthProvider, ThemeProvider, useTelemetryProps } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { AppPropsWithLayout } from 'types'
|
||||
import { CommandMenuProvider } from 'ui'
|
||||
import components from '~/components'
|
||||
import Favicons from '~/components/Favicons'
|
||||
import SiteLayout from '~/layouts/SiteLayout'
|
||||
import { IS_PLATFORM, LOCAL_SUPABASE } from '~/lib/constants'
|
||||
import { API_URL, IS_PLATFORM, LOCAL_SUPABASE } from '~/lib/constants'
|
||||
import { post } from '~/lib/fetchWrappers'
|
||||
import '../styles/ch.scss'
|
||||
import '../styles/main.scss?v=1.0.0'
|
||||
@@ -19,18 +16,26 @@ import '../styles/prism-okaidia.scss'
|
||||
|
||||
function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
const router = useRouter()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
|
||||
const [supabase] = useState(() =>
|
||||
IS_PLATFORM || LOCAL_SUPABASE ? createBrowserSupabaseClient() : undefined
|
||||
)
|
||||
|
||||
function handlePageTelemetry(route: string) {
|
||||
return post(`https://api.supabase.io/platform/telemetry/page`, {
|
||||
referrer: document.referrer,
|
||||
title: document.title,
|
||||
route,
|
||||
})
|
||||
}
|
||||
const handlePageTelemetry = useCallback(
|
||||
(route: string) => {
|
||||
return post(`${API_URL}/telemetry/page`, {
|
||||
referrer: document.referrer,
|
||||
title: document.title,
|
||||
route,
|
||||
ga: {
|
||||
screen_resolution: telemetryProps?.screenResolution,
|
||||
language: telemetryProps?.language,
|
||||
},
|
||||
})
|
||||
},
|
||||
[telemetryProps]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
function handleRouteChange(url: string) {
|
||||
@@ -57,16 +62,35 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
return () => {
|
||||
router.events.off('routeChangeComplete', handleRouteChange)
|
||||
}
|
||||
}, [router.events])
|
||||
}, [router, handlePageTelemetry])
|
||||
|
||||
useEffect(() => {
|
||||
/**
|
||||
* Send page telemetry on first page load
|
||||
*/
|
||||
if (router.isReady) {
|
||||
handlePageTelemetry(router.asPath)
|
||||
handlePageTelemetry(router.basePath + router.asPath)
|
||||
}
|
||||
}, [router.isReady])
|
||||
}, [router, handlePageTelemetry])
|
||||
|
||||
/**
|
||||
* Reference docs use `history.pushState()` to jump to
|
||||
* sub-sections without causing a re-render.
|
||||
*
|
||||
* We need to the below handler to manually force a re-render
|
||||
* when navigating away from, then back to reference docs
|
||||
*/
|
||||
useEffect(() => {
|
||||
function handler() {
|
||||
router.replace(window.location.href)
|
||||
}
|
||||
|
||||
window.addEventListener('popstate', handler)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('popstate', handler)
|
||||
}
|
||||
}, [router])
|
||||
|
||||
const SITE_TITLE = 'Supabase Documentation'
|
||||
|
||||
@@ -85,12 +109,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
<Favicons />
|
||||
<AuthContainer>
|
||||
<ThemeProvider>
|
||||
<CommandMenuProvider
|
||||
site="docs"
|
||||
MarkdownHandler={(props) => (
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} components={components} {...props} />
|
||||
)}
|
||||
>
|
||||
<CommandMenuProvider site="docs">
|
||||
<SiteLayout>
|
||||
<Component {...pageProps} />
|
||||
</SiteLayout>
|
||||
|
||||
@@ -32,6 +32,7 @@ Supabase provides a RESTful API using [PostgREST](https://postgrest.org/). This
|
||||
It provides everything you need from a CRUD API at the URL `https://<project_ref>.supabase.co/rest/v1/`.
|
||||
|
||||
The REST interface is automatically reflected from your database's schema and supports:
|
||||
|
||||
- Basic CRUD operations (Create/Read/Update/Delete)
|
||||
- Arbitrarily deep relationships among tables/views, functions that return table types can also nest related tables/views.
|
||||
- Works with Postgres Views, Materialized Views and Foreign Tables
|
||||
@@ -42,6 +43,7 @@ The REST interface is automatically reflected from your database's schema and su
|
||||
The REST API resolves all requests to a single SQL statement leading to fast response times and high throughput.
|
||||
|
||||
Reference:
|
||||
|
||||
- [Docs](https://postgrest.org/)
|
||||
- [Source Code](https://github.com/PostgREST/postgrest)
|
||||
|
||||
@@ -52,6 +54,7 @@ You can introspect and query the GraphQL API of an existing Supabase project wit
|
||||
or navigate there manually at `API Docs > GraphQL > GraphiQL`.
|
||||
|
||||
The GraphQL interface is automatically reflected from your database's schema and supports:
|
||||
|
||||
- Basic CRUD operations (Create/Read/Update/Delete)
|
||||
- Support for Tables, Views, Materialized Views, and Foreign Tables
|
||||
- Arbitrarily deep relationships among tables/views
|
||||
@@ -61,6 +64,7 @@ The GraphQL interface is automatically reflected from your database's schema and
|
||||
The GraphQL API resolves all requests in a single round-trip leading to fast response times and high throughput.
|
||||
|
||||
Reference:
|
||||
|
||||
- [Docs](https://supabase.github.io/pg_graphql/)
|
||||
- [Source Code](https://github.com/supabase/pg_graphql)
|
||||
|
||||
|
||||
@@ -40,24 +40,25 @@ Let's use an example database that stores `countries` and `cities`:
|
||||
<TabPanel id="SQL" label="SQL">
|
||||
|
||||
```sql
|
||||
create table
|
||||
countries ("id" serial primary key, "name" text);
|
||||
create table countries (
|
||||
"id" serial primary key,
|
||||
"name" text
|
||||
);
|
||||
|
||||
insert into
|
||||
countries (id, name)
|
||||
insert into countries
|
||||
(id, name)
|
||||
values
|
||||
(1, 'United Kingdom'),
|
||||
(2, 'United States');
|
||||
|
||||
create table
|
||||
cities (
|
||||
"id" serial primary key,
|
||||
"name" text,
|
||||
"country_id" int references "countries"
|
||||
);
|
||||
create table cities (
|
||||
"id" serial primary key,
|
||||
"name" text,
|
||||
"country_id" int references "countries"
|
||||
);
|
||||
|
||||
insert into
|
||||
cities (name, country_id)
|
||||
insert into cities
|
||||
(name, country_id)
|
||||
values
|
||||
('London', 1),
|
||||
('Manchester', 1),
|
||||
@@ -130,18 +131,21 @@ GET https://[REF].supabase.co/rest/v1/countries?select=id,name,cities(id,name)
|
||||
The Serverless APIs will detect many-to-many joins. For example, if you have a database which stored teams of users (where each user could belong to many teams):
|
||||
|
||||
```sql
|
||||
create table
|
||||
users ("id" serial primary key, "name" text);
|
||||
create table users (
|
||||
"id" serial primary key,
|
||||
"name" text
|
||||
);
|
||||
|
||||
create table
|
||||
teams ("id" serial primary key, "team_name" text);
|
||||
create table teams (
|
||||
"id" serial primary key,
|
||||
"team_name" text
|
||||
);
|
||||
|
||||
create table
|
||||
members (
|
||||
"user_id" int references users,
|
||||
"team_id" int references teams,
|
||||
primary key(user_id, team_id)
|
||||
);
|
||||
create table members (
|
||||
"user_id" int references users,
|
||||
"team_id" int references teams,
|
||||
primary key (user_id, team_id)
|
||||
);
|
||||
```
|
||||
|
||||
In these cases you don't need to explicitly define the joining table (members). If we wanted to fetch all the teams and the members in each team:
|
||||
|
||||
@@ -49,10 +49,8 @@ To confirm that safeupdate is enabled for API queries, run the following query:
|
||||
select
|
||||
usename,
|
||||
useconfig
|
||||
from
|
||||
pg_shadow
|
||||
where
|
||||
usename = 'authenticator';
|
||||
from pg_shadow
|
||||
where usename = 'authenticator';
|
||||
```
|
||||
|
||||
The expected value for `useconfig` should be:
|
||||
|
||||
@@ -7,7 +7,7 @@ export const meta = {
|
||||
video: 'https://www.youtube.com/v/em1cpOAXknM',
|
||||
}
|
||||
|
||||
Supabase provides you with the option of adding captcha to your sign-in, sign-up, and password reset forms. This keeps your website safe from bots and malicious scripts. Supabase authentication has support for [hCaptcha](https://www.hcaptcha.com/).
|
||||
Supabase provides you with the option of adding captcha to your sign-in, sign-up, and password reset forms. This keeps your website safe from bots and malicious scripts. Supabase authentication has support for [hCaptcha](https://www.hcaptcha.com/) and [Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/).
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
@@ -18,8 +18,15 @@ Supabase provides you with the option of adding captcha to your sign-in, sign-up
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
## Sign up for hCaptcha
|
||||
## Sign up for Captcha
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="hcaptcha-1"
|
||||
>
|
||||
<TabPanel id="hcaptcha-1" label="HCaptcha">
|
||||
Go to the [hCaptcha](https://www.hcaptcha.com/) website and sign up for an account. On the welcome page, copy the **Sitekey** and **Secret key**.
|
||||
|
||||
If you have already signed up and didn't copy this information from the welcome page, you can get the **Secret key** from the settings page.
|
||||
@@ -34,17 +41,34 @@ In the Settings page, look for the **Sitekey** section and copy the key.
|
||||
|
||||

|
||||
|
||||
## Enable hCaptcha protection for your Supabase project
|
||||
</TabPanel>
|
||||
|
||||
Navigate to the **[Authentication](https://app.supabase.com/project/_/settings/auth)** page in the Supabase Dashboard and find the **Enable hCaptcha protection** toggle under the **Security and Protection** section.
|
||||
<TabPanel id="turnstile-1" label="Turnstile">
|
||||
Go to the [Cloudflare website](https://dash.cloudflare.com/login) and sign up for an account. On the welcome page, head to the Turnstile section and add a new site. Create a site and take note of the **Sitekey** and **Secret Key** as shown below
|
||||

|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Enable Captcha protection for your Supabase project
|
||||
|
||||
Navigate to the **[Authentication](https://app.supabase.com/project/_/settings/auth)** page in the Supabase Dashboard and find the **Enable Captcha protection** toggle under the **Security and Protection** section.
|
||||
|
||||

|
||||
|
||||
Enter your hCaptcha **Secret key** and click **Save**.
|
||||
Enter your Captcha **Secret key** and click **Save**.
|
||||
|
||||
## Add the hCaptcha frontend component
|
||||
## Add the Captcha frontend component
|
||||
|
||||
The frontend requires some changes to provide the captcha on-screen for the user. This example uses React and the hCaptcha React component, but hCaptcha can be used with any JavaScript framework.
|
||||
The frontend requires some changes to provide the captcha on-screen for the user. This example uses React and the corresponding Captcha React component, but both Captcha providers can be used with any JavaScript framework.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="hcaptcha-2"
|
||||
>
|
||||
|
||||
<TabPanel id="hcaptcha-2" label="HCaptcha">
|
||||
|
||||
Install `@hcaptcha/react-hcaptcha` in your project as a dependency.
|
||||
|
||||
@@ -58,7 +82,7 @@ Now import the `HCaptcha` component from the `@hcaptcha/react-hcaptcha` library.
|
||||
import HCaptcha from '@hcaptcha/react-hcaptcha'
|
||||
```
|
||||
|
||||
Let’s create a empty state to store our `captchaToken`
|
||||
Let's create a empty state to store our `captchaToken`
|
||||
|
||||
```jsx
|
||||
const [captchaToken, setCaptchaToken] = useState()
|
||||
@@ -75,7 +99,7 @@ We will pass it the sitekey we copied from the hCaptcha website as a property al
|
||||
```jsx
|
||||
<HCaptcha
|
||||
sitekey="your-sitekey"
|
||||
onVerify={(token) => { setCaptchaToken(token) }
|
||||
onVerify={(token) => { setCaptchaToken(token) }
|
||||
/>
|
||||
```
|
||||
|
||||
@@ -117,6 +141,60 @@ captcha.current.resetCaptcha()
|
||||
|
||||
In order to test that this works locally we will need to use something like [ngrok](https://ngrok.com/) or add an entry to your hosts file. You can read more about this in the [hCaptcha docs](https://docs.hcaptcha.com/#local-development).
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="turnstile-2" label="Turnstile">
|
||||
|
||||
The frontend requires some changes to provide the captcha on-screen for the user. This example uses React and the Turnstile React component, but Turnstile can be used with any JavaScript framework.
|
||||
|
||||
Install @marsidev/react-turnstile in your project as a dependency.
|
||||
|
||||
```bash
|
||||
npm install @marsidev/react-turnstile
|
||||
```
|
||||
|
||||
Now import the Turnstile component from the @marsidev/react-turnstile library.
|
||||
|
||||
```jsx
|
||||
import { Turnstile } from '@marsidev/react-turnstile'
|
||||
```
|
||||
|
||||
Let's create an empty state to store our `captchaToken`
|
||||
|
||||
```jsx
|
||||
const [captchaToken, setCaptchaToken] = useState()
|
||||
```
|
||||
|
||||
Now lets add the Cloudflare Turnstile component to the JSX section of our code
|
||||
|
||||
```html
|
||||
<Turnstile />
|
||||
```
|
||||
|
||||
We will pass it the sitekey we copied from the Cloudflare website as a property along with a `onVerify` property which takes a callback function. This callback function will have a token as one of its properties. Let's set the token in the state using `setCaptchaToken`
|
||||
|
||||
```jsx
|
||||
<Turnstile
|
||||
sitekey="your-sitekey"
|
||||
onVerify={(token) => { setCaptchaToken(token) }
|
||||
/>
|
||||
```
|
||||
|
||||
We can now use the captchaToken we receive in our Supabase `signUp` function.
|
||||
|
||||
```jsx
|
||||
await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: { captchaToken },
|
||||
})
|
||||
```
|
||||
|
||||
To test locally, you will need to add localhost to the domain whitelist as per the [Cloudflare docs](https://developers.cloudflare.com/turnstile/reference/testing/)
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
Run the application and you should now be provided with a captcha challenge.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
@@ -94,7 +94,7 @@ The Auth component is currently shipped with the following views:
|
||||
- Update password
|
||||
- Forgotten password
|
||||
|
||||
We are planning on adding more views in the future. Follow along on that [repo](https://github.com/supabase/auth-ui).
|
||||
We are planning on adding more views in the future. Follow along on that [repo](https://github.com/supabase/auth-ui).
|
||||
|
||||
## Customization
|
||||
|
||||
@@ -202,10 +202,7 @@ See the list of [tokens within a theme](https://github.com/supabase/auth-ui/blob
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { Auth } from '@supabase/auth-ui-react'
|
||||
|
||||
const supabase = createClient(
|
||||
'<INSERT PROJECT URL>',
|
||||
'<INSERT PROJECT ANON API KEY>'
|
||||
)
|
||||
const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API KEY>')
|
||||
|
||||
const customTheme = {
|
||||
default: {
|
||||
@@ -214,6 +211,7 @@ const customTheme = {
|
||||
brandAccent: 'hsl(154 54.8% 45.1%)',
|
||||
brandButtonText: 'white',
|
||||
// ..
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
colors: {
|
||||
@@ -238,7 +236,7 @@ const App = () => (
|
||||
<Auth
|
||||
supabaseClient={supabase}
|
||||
theme="default" // can also be "dark" or "evenDarker"
|
||||
appearance={{ theme: customTheme}}
|
||||
appearance={{ theme: customTheme }}
|
||||
/>
|
||||
)
|
||||
```
|
||||
|
||||
@@ -10,7 +10,7 @@ export const meta = {
|
||||
|
||||
The Next.js Auth Helpers package configures Supabase Auth to store the user's session in a cookie, rather than `localStorage`. This makes the users's session available server-side - in Server Components and Route Handlers - and is automatically sent along with any requests to Supabase.
|
||||
|
||||
> The `app` directory in Next.js is still in beta and expected to change. For examples using the `pages` directory check out [Auth Helpers in Next.js](/docs/guides/auth/auth-helpers/nextjs).
|
||||
> Note: If you are using the `pages` directory, check out [Auth Helpers in Next.js](/docs/guides/auth/auth-helpers/nextjs).
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
@@ -38,8 +38,6 @@ The Next.js Auth Helpers package configures Supabase Auth to store the user's se
|
||||
npm install @supabase/auth-helpers-nextjs
|
||||
```
|
||||
|
||||
> [Next.js Server Components](https://beta.nextjs.org/docs) and the `app` directory are experimental and likely to change.
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="yarn" label="Yarn">
|
||||
|
||||
@@ -47,8 +45,6 @@ npm install @supabase/auth-helpers-nextjs
|
||||
yarn add @supabase/auth-helpers-nextjs
|
||||
```
|
||||
|
||||
> [Next.js Server Components](https://beta.nextjs.org/docs) and the `app` directory are experimental and likely to change.
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
@@ -138,7 +134,7 @@ import { useRouter } from 'next/navigation'
|
||||
|
||||
const Context = createContext(undefined)
|
||||
|
||||
export default function SupabaseProvider({ children }) {
|
||||
export default function SupabaseProvider({ children, session }) {
|
||||
const [supabase] = useState(() => createBrowserSupabaseClient())
|
||||
const router = useRouter()
|
||||
|
||||
@@ -155,7 +151,7 @@ export default function SupabaseProvider({ children }) {
|
||||
}, [router, supabase])
|
||||
|
||||
return (
|
||||
<Context.Provider value={{ supabase }}>
|
||||
<Context.Provider value={{ supabase, session }}>
|
||||
<>{children}</>
|
||||
</Context.Provider>
|
||||
)
|
||||
@@ -182,19 +178,28 @@ Create a new file at `/app/supabase-provider.tsx` and populate with the followin
|
||||
'use client'
|
||||
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { Session, createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
import type { SupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
type MaybeSession = Session | null
|
||||
|
||||
type SupabaseContext = {
|
||||
supabase: SupabaseClient<Database>
|
||||
session: MaybeSession
|
||||
}
|
||||
|
||||
const Context = createContext<SupabaseContext | undefined>(undefined)
|
||||
|
||||
export default function SupabaseProvider({ children }: { children: React.ReactNode }) {
|
||||
export default function SupabaseProvider({
|
||||
children,
|
||||
session,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
session: MaybeSession
|
||||
}) {
|
||||
const [supabase] = useState(() => createBrowserSupabaseClient())
|
||||
const router = useRouter()
|
||||
|
||||
@@ -211,7 +216,7 @@ export default function SupabaseProvider({ children }: { children: React.ReactNo
|
||||
}, [router, supabase])
|
||||
|
||||
return (
|
||||
<Context.Provider value={{ supabase }}>
|
||||
<Context.Provider value={{ supabase, session }}>
|
||||
<>{children}</>
|
||||
</Context.Provider>
|
||||
)
|
||||
@@ -256,7 +261,10 @@ export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<SupabaseProvider>{children}</SupabaseProvider>
|
||||
<SupabaseProvider session={session}>
|
||||
<SupabaseListener serverAccessToken={session?.access_token} />
|
||||
{children}
|
||||
</SupabaseProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
@@ -282,7 +290,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<SupabaseProvider>{children}</SupabaseProvider>
|
||||
<SupabaseProvider session={session}>
|
||||
<SupabaseListener serverAccessToken={session?.access_token} />
|
||||
{children}
|
||||
</SupabaseProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@ This library supports the following tooling versions:
|
||||
- Node.js: `^10.13.0 || >=12.0.0`
|
||||
- Next.js: `>=10`
|
||||
|
||||
> Note: Next.js 13 is stable, however, the new `app` directory and Server Components are still in beta. Check out our experimental guide on [using Auth Helpers with Next.js Server Components](/docs/guides/auth/auth-helpers/nextjs-server-components).
|
||||
> Note: As of [Next.js 13.4](https://nextjs.org/blog/next-13-4), the `app` directory and Server Components have reached stable status. Check out our guide on using [Auth Helpers with Next.js Server Components](/docs/guides/auth/auth-helpers/nextjs-server-components).
|
||||
|
||||
Additionally, install the **React Auth Helpers** for components and hooks that can be used across all React-based frameworks.
|
||||
|
||||
|
||||
@@ -424,9 +424,7 @@ And then use the revalidator, inside the `onAuthStateChange` hook.
|
||||
const { env, session } = useLoaderData()
|
||||
const { revalidate } = useRevalidator()
|
||||
|
||||
const [supabase] = useState(() =>
|
||||
createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY)
|
||||
)
|
||||
const [supabase] = useState(() => createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY))
|
||||
|
||||
const serverAccessToken = session?.access_token
|
||||
|
||||
@@ -464,7 +462,7 @@ useEffect(() => {
|
||||
const {
|
||||
data: { subscription },
|
||||
} = supabase.auth.onAuthStateChange((event, session) => {
|
||||
if (event !== "INITIAL_SESSION" && session?.access_token !== serverAccessToken) {
|
||||
if (event !== 'INITIAL_SESSION' && session?.access_token !== serverAccessToken) {
|
||||
// server and client are out of sync.
|
||||
revalidate()
|
||||
}
|
||||
|
||||
@@ -35,19 +35,16 @@ Create a server supabase client in a handle hook:
|
||||
|
||||
```ts title=src/hooks.server.ts
|
||||
// src/hooks.server.ts
|
||||
import {
|
||||
PUBLIC_SUPABASE_URL,
|
||||
PUBLIC_SUPABASE_ANON_KEY
|
||||
} from '$env/static/public';
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit';
|
||||
import type { Handle } from '@sveltejs/kit';
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { Handle } from '@sveltejs/kit'
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.supabase = createSupabaseServerClient({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event
|
||||
});
|
||||
event,
|
||||
})
|
||||
|
||||
/**
|
||||
* a little helper that is written for convenience so that instead
|
||||
@@ -56,10 +53,10 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
*/
|
||||
event.locals.getSession = async () => {
|
||||
const {
|
||||
data: { session }
|
||||
} = await event.locals.supabase.auth.getSession();
|
||||
return session;
|
||||
};
|
||||
data: { session },
|
||||
} = await event.locals.supabase.auth.getSession()
|
||||
return session
|
||||
}
|
||||
|
||||
return resolve(event, {
|
||||
/**
|
||||
@@ -68,10 +65,10 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
* https://github.com/sveltejs/kit/issues/8061
|
||||
*/
|
||||
filterSerializedResponseHeaders(name) {
|
||||
return name === 'content-range';
|
||||
}
|
||||
});
|
||||
};
|
||||
return name === 'content-range'
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
> Note that we are specifying filterSerializedResponseHeaders here. We need to tell SvelteKit that supabase needs the content-range header.
|
||||
@@ -82,13 +79,13 @@ In order to make the session available to the UI (pages, layouts) we need to pas
|
||||
|
||||
```ts title=src/routes/+layout.server.ts
|
||||
// src/routes/+layout.server.ts
|
||||
import type { LayoutServerLoad } from './$types';
|
||||
import type { LayoutServerLoad } from './$types'
|
||||
|
||||
export const load: LayoutServerLoad = async ({ locals: { getSession } }) => {
|
||||
return {
|
||||
session: await getSession()
|
||||
};
|
||||
};
|
||||
session: await getSession(),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Shared Load functions and pages
|
||||
@@ -97,30 +94,27 @@ To be able to use Supabase in shared load functions and inside pages you need to
|
||||
|
||||
```ts
|
||||
// src/routes/+layout.ts
|
||||
import {
|
||||
PUBLIC_SUPABASE_ANON_KEY,
|
||||
PUBLIC_SUPABASE_URL
|
||||
} from '$env/static/public';
|
||||
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import type { Database } from '../DatabaseDefinitions';
|
||||
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
|
||||
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { LayoutLoad } from './$types'
|
||||
import type { Database } from '../DatabaseDefinitions'
|
||||
|
||||
export const load: LayoutLoad = async ({ fetch, data, depends }) => {
|
||||
depends('supabase:auth');
|
||||
depends('supabase:auth')
|
||||
|
||||
const supabase = createSupabaseLoadClient<Database>({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event: { fetch },
|
||||
serverSession: data.session
|
||||
});
|
||||
serverSession: data.session,
|
||||
})
|
||||
|
||||
const {
|
||||
data: { session }
|
||||
} = await supabase.auth.getSession();
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
return { supabase, session };
|
||||
};
|
||||
return { supabase, session }
|
||||
}
|
||||
```
|
||||
|
||||
Access the client inside pages by `$page.data.supabase` or `data.supabase` when using `export let data: PageData`.
|
||||
@@ -169,17 +163,17 @@ In order to get the most out of TypeScript and it's intellisense, you should imp
|
||||
```ts
|
||||
// src/app.d.ts
|
||||
|
||||
import { SupabaseClient, Session } from '@supabase/supabase-js';
|
||||
import { Database } from './DatabaseDefinitions';
|
||||
import { SupabaseClient, Session } from '@supabase/supabase-js'
|
||||
import { Database } from './DatabaseDefinitions'
|
||||
|
||||
declare global {
|
||||
namespace App {
|
||||
interface Locals {
|
||||
supabase: SupabaseClient<Database>;
|
||||
getSession(): Promise<Session | null>;
|
||||
supabase: SupabaseClient<Database>
|
||||
getSession(): Promise<Session | null>
|
||||
}
|
||||
interface PageData {
|
||||
session: Session | null;
|
||||
session: Session | null
|
||||
}
|
||||
// interface Error {}
|
||||
// interface Platform {}
|
||||
@@ -193,24 +187,24 @@ For [row level security](https://supabase.com/docs/guides/auth/row-level-securit
|
||||
|
||||
```html
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import type { PageData } from './$types'
|
||||
|
||||
export let data: PageData;
|
||||
export let data: PageData
|
||||
|
||||
let loadedData = [];
|
||||
let loadedData = []
|
||||
async function loadData() {
|
||||
const { data: result } = await data.supabase.from('test').select('*').limit(20);
|
||||
loadedData = result;
|
||||
const { data: result } = await data.supabase.from('test').select('*').limit(20)
|
||||
loadedData = result
|
||||
}
|
||||
|
||||
$: if (data.session) {
|
||||
loadData();
|
||||
loadData()
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if data.session}
|
||||
<p>client-side data fetching with RLS</p>
|
||||
<pre>{JSON.stringify(loadedData, null, 2)}</pre>
|
||||
<p>client-side data fetching with RLS</p>
|
||||
<pre>{JSON.stringify(loadedData, null, 2)}</pre>
|
||||
{/if}
|
||||
```
|
||||
|
||||
@@ -219,10 +213,10 @@ For [row level security](https://supabase.com/docs/guides/auth/row-level-securit
|
||||
```html
|
||||
<!-- src/routes/profile/+page.svelte -->
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import type { PageData } from './$types'
|
||||
|
||||
export let data: PageData;
|
||||
$: ({ user, tableData } = data);
|
||||
export let data: PageData
|
||||
$: ({ user, tableData } = data)
|
||||
</script>
|
||||
|
||||
<div>Protected content for {user.email}</div>
|
||||
@@ -232,21 +226,21 @@ For [row level security](https://supabase.com/docs/guides/auth/row-level-securit
|
||||
|
||||
```ts
|
||||
// src/routes/profile/+page.ts
|
||||
import type { PageLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types'
|
||||
import { redirect } from '@sveltejs/kit'
|
||||
|
||||
export const load: PageLoad = async ({ parent }) => {
|
||||
const { supabase, session } = await parent();
|
||||
const { supabase, session } = await parent()
|
||||
if (!session) {
|
||||
throw redirect(303, '/');
|
||||
throw redirect(303, '/')
|
||||
}
|
||||
const { data: tableData } = await supabase.from('test').select('*');
|
||||
const { data: tableData } = await supabase.from('test').select('*')
|
||||
|
||||
return {
|
||||
user: session.user,
|
||||
tableData
|
||||
};
|
||||
};
|
||||
tableData,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Protecting API routes
|
||||
@@ -255,21 +249,19 @@ Wrap an API Route to check that the user has a valid session. If they're not log
|
||||
|
||||
```ts
|
||||
// src/routes/api/protected-route/+server.ts
|
||||
import type { RequestHandler } from './$types';
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types'
|
||||
import { json, error } from '@sveltejs/kit'
|
||||
|
||||
export const GET: RequestHandler = async ({
|
||||
locals: { supabase, getSession }
|
||||
}) => {
|
||||
const session = await getSession();
|
||||
export const GET: RequestHandler = async ({ locals: { supabase, getSession } }) => {
|
||||
const session = await getSession()
|
||||
if (!session) {
|
||||
// the user is not signed in
|
||||
throw error(401, { message: 'Unauthorized' });
|
||||
throw error(401, { message: 'Unauthorized' })
|
||||
}
|
||||
const { data } = await supabase.from('test').select('*');
|
||||
const { data } = await supabase.from('test').select('*')
|
||||
|
||||
return json({ data });
|
||||
};
|
||||
return json({ data })
|
||||
}
|
||||
```
|
||||
|
||||
If you visit `/api/protected-route` without a valid session cookie, you will get a 401 response.
|
||||
@@ -280,35 +272,35 @@ Wrap an Action to check that the user has a valid session. If they're not logged
|
||||
|
||||
```ts
|
||||
// src/routes/posts/+page.server.ts
|
||||
import type { Actions } from './$types';
|
||||
import { error, fail } from '@sveltejs/kit';
|
||||
import type { Actions } from './$types'
|
||||
import { error, fail } from '@sveltejs/kit'
|
||||
|
||||
export const actions: Actions = {
|
||||
createPost: async ({ request, locals: { supabase, getSession } }) => {
|
||||
const session = await getSession();
|
||||
const session = await getSession()
|
||||
|
||||
if (!session) {
|
||||
// the user is not signed in
|
||||
throw error(401, { message: 'Unauthorized' });
|
||||
throw error(401, { message: 'Unauthorized' })
|
||||
}
|
||||
// we are save, let the user create the post
|
||||
const formData = await request.formData();
|
||||
const content = formData.get('content');
|
||||
const formData = await request.formData()
|
||||
const content = formData.get('content')
|
||||
|
||||
const { error: createPostError, data: newPost } = await supabase
|
||||
.from('posts')
|
||||
.insert({ content });
|
||||
.insert({ content })
|
||||
|
||||
if (createPostError) {
|
||||
return fail(500, {
|
||||
supabaseErrorMessage: createPostError.message
|
||||
});
|
||||
supabaseErrorMessage: createPostError.message,
|
||||
})
|
||||
}
|
||||
return {
|
||||
newPost
|
||||
};
|
||||
}
|
||||
};
|
||||
newPost,
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If you try to submit a form with the action `?/createPost` without a valid session cookie, you will get a 401 error response.
|
||||
@@ -316,47 +308,47 @@ If you try to submit a form with the action `?/createPost` without a valid sessi
|
||||
## Saving and deleting the session
|
||||
|
||||
```ts
|
||||
import type { Actions } from './$types';
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { AuthApiError } from '@supabase/supabase-js';
|
||||
import type { Actions } from './$types'
|
||||
import { fail, redirect } from '@sveltejs/kit'
|
||||
import { AuthApiError } from '@supabase/supabase-js'
|
||||
|
||||
export const actions: Actions = {
|
||||
signin: async ({ request, locals: { supabase } }) => {
|
||||
const formData = await request.formData();
|
||||
const formData = await request.formData()
|
||||
|
||||
const email = formData.get('email') as string;
|
||||
const password = formData.get('password') as string;
|
||||
const email = formData.get('email') as string
|
||||
const password = formData.get('password') as string
|
||||
|
||||
const { error } = await supabase.auth.signInWithPassword({
|
||||
email,
|
||||
password
|
||||
});
|
||||
password,
|
||||
})
|
||||
|
||||
if (error) {
|
||||
if (error instanceof AuthApiError && error.status === 400) {
|
||||
return fail(400, {
|
||||
error: 'Invalid credentials.',
|
||||
values: {
|
||||
email
|
||||
}
|
||||
});
|
||||
email,
|
||||
},
|
||||
})
|
||||
}
|
||||
return fail(500, {
|
||||
error: 'Server error. Try again later.',
|
||||
values: {
|
||||
email
|
||||
}
|
||||
});
|
||||
email,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
throw redirect(303, '/dashboard');
|
||||
throw redirect(303, '/dashboard')
|
||||
},
|
||||
|
||||
signout: async ({ locals: { supabase } }) => {
|
||||
await supabase.auth.signOut();
|
||||
throw redirect(303, '/');
|
||||
}
|
||||
};
|
||||
await supabase.auth.signOut()
|
||||
throw redirect(303, '/')
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Protecting multiple routes
|
||||
@@ -366,33 +358,30 @@ protect multiple routes at once.
|
||||
|
||||
```ts
|
||||
// src/hooks.server.ts
|
||||
import type { RequestHandler } from './$types';
|
||||
import { redirect, error } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types'
|
||||
import { redirect, error } from '@sveltejs/kit'
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
// protect requests to all routes that start with /protected-routes
|
||||
if (event.url.pathname.startsWith('/protected-routes')) {
|
||||
const session = await event.locals.getSession();
|
||||
const session = await event.locals.getSession()
|
||||
if (!session) {
|
||||
// the user is not signed in
|
||||
throw redirect(303, '/');
|
||||
throw redirect(303, '/')
|
||||
}
|
||||
}
|
||||
|
||||
// protect POST requests to all routes that start with /protected-posts
|
||||
if (
|
||||
event.url.pathname.startsWith('/protected-posts') &&
|
||||
event.request.method === 'POST'
|
||||
) {
|
||||
const session = await event.locals.getSession();
|
||||
if (event.url.pathname.startsWith('/protected-posts') && event.request.method === 'POST') {
|
||||
const session = await event.locals.getSession()
|
||||
if (!session) {
|
||||
// the user is not signed in
|
||||
throw error(303, '/');
|
||||
throw error(303, '/')
|
||||
}
|
||||
}
|
||||
|
||||
return resolve(event);
|
||||
};
|
||||
return resolve(event)
|
||||
}
|
||||
```
|
||||
|
||||
## Migrate from 0.8.x to 0.9 [#migration]
|
||||
@@ -425,19 +414,16 @@ export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_S
|
||||
|
||||
```js title=src/hooks.server.ts
|
||||
// src/hooks.server.ts
|
||||
import {
|
||||
PUBLIC_SUPABASE_URL,
|
||||
PUBLIC_SUPABASE_ANON_KEY
|
||||
} from '$env/static/public';
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit';
|
||||
import type { Handle } from '@sveltejs/kit';
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { Handle } from '@sveltejs/kit'
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.supabase = createSupabaseServerClient({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event
|
||||
});
|
||||
event,
|
||||
})
|
||||
|
||||
/**
|
||||
* a little helper that is written for convenience so that instead
|
||||
@@ -446,10 +432,10 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
*/
|
||||
event.locals.getSession = async () => {
|
||||
const {
|
||||
data: { session }
|
||||
} = await event.locals.supabase.auth.getSession();
|
||||
return session;
|
||||
};
|
||||
data: { session },
|
||||
} = await event.locals.supabase.auth.getSession()
|
||||
return session
|
||||
}
|
||||
|
||||
return resolve(event, {
|
||||
/**
|
||||
@@ -458,10 +444,10 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
* https://github.com/sveltejs/kit/issues/8061
|
||||
*/
|
||||
filterSerializedResponseHeaders(name) {
|
||||
return name === 'content-range';
|
||||
}
|
||||
});
|
||||
};
|
||||
return name === 'content-range'
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
@@ -469,7 +455,7 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
|
||||
### Initialize the client [#migration-initialize-client]
|
||||
|
||||
In order to use the Supabase library in your client code you will need to setup a shared load function inside the root `+layout.ts` and create a `+layout.svelte` to handle our event listening for Auth events.
|
||||
In order to use the Supabase library in your client code you will need to setup a shared load function inside the root `+layout.ts` and create a `+layout.svelte` to handle our event listening for Auth events.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
@@ -507,31 +493,28 @@ In order to use the Supabase library in your client code you will need to setup
|
||||
|
||||
```ts title=src/routes/+layout.ts
|
||||
// src/routes/+layout.ts
|
||||
import { invalidate } from '$app/navigation';
|
||||
import {
|
||||
PUBLIC_SUPABASE_ANON_KEY,
|
||||
PUBLIC_SUPABASE_URL
|
||||
} from '$env/static/public';
|
||||
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import type { Database } from '../DatabaseDefinitions';
|
||||
import { invalidate } from '$app/navigation'
|
||||
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
|
||||
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { LayoutLoad } from './$types'
|
||||
import type { Database } from '../DatabaseDefinitions'
|
||||
|
||||
export const load: LayoutLoad = async ({ fetch, data, depends }) => {
|
||||
depends('supabase:auth');
|
||||
depends('supabase:auth')
|
||||
|
||||
const supabase = createSupabaseLoadClient<Database>({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event: { fetch },
|
||||
serverSession: data.session
|
||||
});
|
||||
serverSession: data.session,
|
||||
})
|
||||
|
||||
const {
|
||||
data: { session }
|
||||
} = await supabase.auth.getSession();
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
return { supabase, session };
|
||||
};
|
||||
return { supabase, session }
|
||||
}
|
||||
```
|
||||
|
||||
```svelte title=src/routes/+layout.svelte
|
||||
@@ -605,17 +588,17 @@ declare namespace App {
|
||||
|
||||
```ts title=src/app.d.ts
|
||||
// src/app.d.ts
|
||||
import { SupabaseClient, Session } from '@supabase/supabase-js';
|
||||
import { Database } from './DatabaseDefinitions';
|
||||
import { SupabaseClient, Session } from '@supabase/supabase-js'
|
||||
import { Database } from './DatabaseDefinitions'
|
||||
|
||||
declare global {
|
||||
namespace App {
|
||||
interface Locals {
|
||||
supabase: SupabaseClient<Database>;
|
||||
getSession(): Promise<Session | null>;
|
||||
supabase: SupabaseClient<Database>
|
||||
getSession(): Promise<Session | null>
|
||||
}
|
||||
interface PageData {
|
||||
session: Session | null;
|
||||
session: Session | null
|
||||
}
|
||||
// interface Error {}
|
||||
// interface Platform {}
|
||||
@@ -675,10 +658,10 @@ export const load: PageLoad = async (event) => {
|
||||
```html title=src/routes/profile/+page.svelte
|
||||
<!-- src/routes/profile/+page.svelte -->
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import type { PageData } from './$types'
|
||||
|
||||
export let data: PageData;
|
||||
$: ({ user, tableData } = data);
|
||||
export let data: PageData
|
||||
$: ({ user, tableData } = data)
|
||||
</script>
|
||||
|
||||
<div>Protected content for {user.email}</div>
|
||||
@@ -688,21 +671,21 @@ export const load: PageLoad = async (event) => {
|
||||
|
||||
```ts title=src/routes/profile/+page.ts
|
||||
// src/routes/profile/+page.ts
|
||||
import type { PageLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageLoad } from './$types'
|
||||
import { redirect } from '@sveltejs/kit'
|
||||
|
||||
export const load: PageLoad = async ({ parent }) => {
|
||||
const { supabase, session } = await parent();
|
||||
const { supabase, session } = await parent()
|
||||
if (!session) {
|
||||
throw redirect(303, '/');
|
||||
throw redirect(303, '/')
|
||||
}
|
||||
const { data: tableData } = await supabase.from('test').select('*');
|
||||
const { data: tableData } = await supabase.from('test').select('*')
|
||||
|
||||
return {
|
||||
user: session.user,
|
||||
tableData
|
||||
};
|
||||
};
|
||||
tableData,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
@@ -740,27 +723,24 @@ export const GET: RequestHandler = async (event) => {
|
||||
|
||||
```ts title=src/routes/api/protected-route/+server.ts
|
||||
// src/routes/api/protected-route/+server.ts
|
||||
import type { RequestHandler } from './$types';
|
||||
import { json, error } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types'
|
||||
import { json, error } from '@sveltejs/kit'
|
||||
|
||||
export const GET: RequestHandler = async ({
|
||||
locals: { supabase, getSession }
|
||||
}) => {
|
||||
const session = await getSession();
|
||||
export const GET: RequestHandler = async ({ locals: { supabase, getSession } }) => {
|
||||
const session = await getSession()
|
||||
if (!session) {
|
||||
// the user is not signed in
|
||||
throw error(401, { message: 'Unauthorized' });
|
||||
throw error(401, { message: 'Unauthorized' })
|
||||
}
|
||||
const { data } = await supabase.from('test').select('*');
|
||||
const { data } = await supabase.from('test').select('*')
|
||||
|
||||
return json({ data });
|
||||
};
|
||||
return json({ data })
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Migrate from 0.7.x to 0.8 [#migration-0-8]
|
||||
|
||||
### Set up the Supabase client [#migration-set-up-supabase-client-0-8]
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
import { Accordion } from 'ui'
|
||||
|
||||
export const meta = {
|
||||
id: 'auth-password-reset',
|
||||
title: 'Password Reset',
|
||||
description: 'How to reset password with Supabase Auth',
|
||||
}
|
||||
|
||||
- Send email for password reset using [`.resetPasswordForEmail`](/docs/reference/javascript/auth-resetpasswordforemail) while providing a `redirectTo` parameter
|
||||
- Email link will work as a magic link and log the user in then take them to the url specified in the `redirectTo` parameter
|
||||
- Create form to update the password and call the [`.updateUser`](/docs/reference/javascript/auth-updateuser) method with the new password
|
||||
|
||||
## Single Page Application (SPA)
|
||||
|
||||
### Sending password reset email
|
||||
Supabase provides a convenient method [`.resetPasswordForEmail`](/docs/reference/javascript/auth-resetpasswordforemail)
|
||||
to reset a user password. This method takes a parameter of `redirectTo` which we will use to pass an absolute URL to
|
||||
the update password page. This URL must be saved in your
|
||||
allowed [Redirect URLs](https://app.supabase.com/project/_/auth/url-configuration) list found at
|
||||
[Authentication > Redirect Configuration](https://app.supabase.com/project/_/auth/url-configuration) or it won't
|
||||
redirect the user.
|
||||
|
||||
```ts
|
||||
await supabase.auth.resetPasswordForEmail('hello@example.com', {
|
||||
redirectTo: 'http://example.com/account/update-password',
|
||||
})
|
||||
```
|
||||
|
||||
### Email link
|
||||
The email link you receive will work like a magic link. This way when you click the link you will be logged into
|
||||
the website. Since we passed a redirect URL to the [`.resetPasswordForEmail`](https://supabase.com/docs/reference/javascript/auth-resetpasswordforemail)
|
||||
method the user should be sent to the update password page.
|
||||
|
||||
### Update Password
|
||||
To update the password we call the [`.updateUser`](/docs/reference/javascript/auth-updateuser) method and pass along the new password to this method.
|
||||
|
||||
```ts
|
||||
await supabase.auth.updateUser({ password: new_password })
|
||||
```
|
||||
|
||||
## Server-Side Rendering (SSR)
|
||||
|
||||
### Sending password reset email
|
||||
Supabase provides a convenient method [`.resetPasswordForEmail`](/docs/reference/javascript/auth-resetpasswordforemail)
|
||||
to reset a user password. This method takes a parameter of `redirectTo` which we will use to pass an absolute URL to
|
||||
the callback page along with a query parameter to the update password page. This URL must be saved in your
|
||||
allowed [Redirect URLs](https://app.supabase.com/project/_/auth/url-configuration) list found at
|
||||
[Authentication > Redirect Configuration](https://app.supabase.com/project/_/auth/url-configuration) or it won't
|
||||
redirect the user.
|
||||
|
||||
```ts
|
||||
await supabase.auth.resetPasswordForEmail('hello@example.com', {
|
||||
redirectTo: 'http://example.com/api/auth/callback?next=/account/update-password',
|
||||
})
|
||||
```
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
We are using `next` as our query parameter, but this can name whatever you like.
|
||||
|
||||
</Admonition>
|
||||
|
||||
### Email link
|
||||
The email link you receive will behave like a magic link. When the link is clicked you will be sent to the `redirectTo` URL you specified that points to the path with the exchange code.
|
||||
|
||||
### Exchange authorization code
|
||||
After redirecting to the server page, we need to retrieve the code from the query parameter called `code` and pass it to the `.exchangeAuthCodeForSession` function.
|
||||
```ts
|
||||
// api/auth/callback.ts
|
||||
|
||||
// The code is retrieved from the query parameter - use whichever mechanism is recommended
|
||||
// for your app/framework. This is just an example.
|
||||
const code = url.searchParams.get('code')
|
||||
|
||||
// call the Supabase API to exchange the code for a session
|
||||
await supabase.auth.exchangeCodeForSession(code)
|
||||
```
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
The query parameter is always `code` for the authorization code returned from the Supabase API
|
||||
|
||||
</Admonition>
|
||||
|
||||
We will also need to check for the `next` query parameter to redirect the user to the update password page.
|
||||
```ts
|
||||
// api/auth/callback.ts
|
||||
|
||||
// The password page path is retrieved from the query parameter - use whichever mechanism is recommended
|
||||
// for your app/framework. This is just an example.
|
||||
const next = url.searchParams.get('next')
|
||||
|
||||
// using NextJS API response object in this example
|
||||
res.redirect(next)
|
||||
```
|
||||
|
||||
### Update Password
|
||||
To update the password we call the [`.updateUser`](/docs/reference/javascript/auth-updateuser) method and
|
||||
pass along the new password to this method.
|
||||
|
||||
```ts
|
||||
await supabase.auth.updateUser({ password: new_password })
|
||||
```
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
|
||||
@@ -98,7 +98,7 @@ export const meta = {
|
||||
} = supabase.auth.onAuthStateChange((_event, session) => {
|
||||
setSession(session)
|
||||
})
|
||||
|
||||
|
||||
return () => subscription.unsubscribe()
|
||||
}, [])
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ puts token
|
||||
1. Edit the `secret_gen.rb` file:
|
||||
|
||||
- `key_file` = "Path to the private key you downloaded from Apple". It should look like this: `AuthKey_XXXXXXXXXX.p8`.
|
||||
- `team_id` = "Your Team ID". This is found at the top right of the Apple Developer site (next to your name).
|
||||
- `team_id` = "Your Team ID". This is found at the Apple Developer website, under Membership details. This is a 10-character alphanumeric string called "Team ID". Alternatively, this can be seen next to your name in the upper right when viewing your Certificates, Identifiers & Profiles.
|
||||
- `client_id` = "The Service ID of the service you created". This is the `Services ID` you created in the above step `Obtain a Services ID`. If you've lost this ID, you can find it in the Apple Developer Site:
|
||||
- Go to `Certificates, Identifiers & Profiles`.
|
||||
- Click `Identifiers` at the left.
|
||||
|
||||
@@ -36,7 +36,7 @@ Go to the [GitHub Developer Settings](https://github.com/settings/developers) pa
|
||||
|
||||
<SocialProviderSetup provider="GitHub" />
|
||||
|
||||
## Register a new OAuth application on Github.
|
||||
## Register a new OAuth application on Github.
|
||||
|
||||
- Navigate to `Settings`/`Developer settings`/`OAuth Apps`
|
||||
- Click `Register a new application`. If you've created an app before, click `New OAuth App` here.
|
||||
|
||||
@@ -30,7 +30,7 @@ Setting up Slack logins for your application consists of 3 parts:
|
||||
## Create a Slack OAuth app
|
||||
|
||||
- Go to [api.slack.com](https://api.slack.com/apps).
|
||||
- Click on `Create an App`
|
||||
- Click on `Create New App`
|
||||
|
||||
Under `Create an app...`:
|
||||
|
||||
@@ -43,14 +43,17 @@ Under `App Credentials`:
|
||||
|
||||
- Copy and save your newly-generated `Client ID`
|
||||
- Copy and save your newly-generated `Client Secret`
|
||||
- Click `Permissions`
|
||||
|
||||
Under `Redirect URLs`:
|
||||
Under the sidebar, select `OAuth & Permissions` and look for `Redirect URLs`:
|
||||
|
||||
- Click `Add New Redirect URL`
|
||||
- Paste your `Callback URL` then click `Add`
|
||||
- Click `Save URLs`
|
||||
|
||||
Under `Scopes`:
|
||||
|
||||
- Add the following scopes under the `User Token Scopes`: `profile`, `email`, `openid`. These scopes are the default scopes that Supabase Auth uses to request for user information. You can add any additional scopes that you may need as well.
|
||||
|
||||
## Enter your Slack credentials into your Supabase Project
|
||||
|
||||
<SocialProviderSettingsSupabase provider="Slack" />
|
||||
|
||||
@@ -105,17 +105,16 @@ A common use case with SSO is to use the UUID of the identity provider as the id
|
||||
For example, let's say you have a table like:
|
||||
|
||||
```sql
|
||||
CREATE TABLE
|
||||
organization_settings (
|
||||
-- the organization's unique ID
|
||||
id UUID NOT NULL PRIMARY KEY,
|
||||
-- the organization's SSO identity provider
|
||||
sso_provider_id UUID UNIQUE,
|
||||
-- name of the organization
|
||||
name TEXT,
|
||||
-- billing plan (paid, free, enterprise)
|
||||
billing_plan TEXT
|
||||
);
|
||||
create table organization_settings (
|
||||
-- the organization's unique ID
|
||||
id uuid not null primary key,
|
||||
-- the organization's SSO identity provider
|
||||
sso_provider_id uuid unique,
|
||||
-- name of the organization
|
||||
name text,
|
||||
-- billing plan (paid, free, enterprise)
|
||||
billing_plan text
|
||||
);
|
||||
```
|
||||
|
||||
You can use the information present in the user's JWT to scope down which rows from this table the user can see, without doing any additional user management:
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'using-environment-variables-in-config',
|
||||
title: 'Using environment variables in config.toml',
|
||||
description: 'How to use environment variables in config.toml with the Supabase CLI.',
|
||||
}
|
||||
|
||||
The Supabase CLI is capable of utilizing environment variables stored in our project's root directory's `.env` file within the `config.toml` file.
|
||||
|
||||
We can reference the environment variable by using the `env()` function.
|
||||
|
||||
Inside of our `.env` file we add the environment variable as we normally would
|
||||
|
||||
```env
|
||||
GITHUB_CLIENT_ID=""
|
||||
GITHUB_SECRET=""
|
||||
```
|
||||
|
||||
And then reference them inside of our `config.toml`
|
||||
|
||||
```toml
|
||||
[auth.external.github]
|
||||
enabled = true
|
||||
client_id = "env(GITHUB_CLIENT_ID)"
|
||||
secret = "env(GITHUB_SECRET)"
|
||||
# Overrides the default auth redirectUrl.
|
||||
redirect_uri = ""
|
||||
```
|
||||
|
||||
These same environment variables will be referenced by the `supabase start` command from the Supabase CLI.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -12,13 +12,13 @@ extension which provides SQL access to
|
||||
algorithms.
|
||||
|
||||
libsodium is a modern, easy-to-use software library for encryption,
|
||||
decryption, signatures, password hashing, and more. It is a portable,
|
||||
decryption, signatures, password hashing, and more. It is a portable,
|
||||
cross-compilable, installable, and packageable fork of the
|
||||
[NaCl](http://nacl.cr.yp.to/) library, with a compatible but extended
|
||||
API to improve usability even further.
|
||||
|
||||
Its goal is to provide all of the core operations needed to build
|
||||
higher-level cryptographic tools. Sodium is cross-platform and
|
||||
higher-level cryptographic tools. Sodium is cross-platform and
|
||||
cross-language. It runs on many compilers and operating systems,
|
||||
including Windows (with MinGW or Visual Studio, x86 and x86_64), iOS,
|
||||
and Android. JavaScript and WebAssembly versions are also available
|
||||
@@ -33,12 +33,12 @@ pgsodium exposes the following libsodium APIs to SQL:
|
||||
|
||||
- [Generating Random Data](https://github.com/michelp/pgsodium/#generating-random-data)
|
||||
- [Secret key cryptography](https://github.com/michelp/pgsodium/#secret-key-cryptography)
|
||||
- [Authenticated encryption](https://github.com/michelp/pgsodium/#authenticated-encryption)
|
||||
- [Authentication](https://github.com/michelp/pgsodium/#authentication)
|
||||
- [Authenticated encryption](https://github.com/michelp/pgsodium/#authenticated-encryption)
|
||||
- [Authentication](https://github.com/michelp/pgsodium/#authentication)
|
||||
- [Public key cryptography](https://github.com/michelp/pgsodium/#public-key-cryptography)
|
||||
- [Authenticated encryption](https://github.com/michelp/pgsodium/#authenticated-encryption-1)
|
||||
- [Public key signatures](https://github.com/michelp/pgsodium/#public-key-signatures)
|
||||
- [Sealed boxes](https://github.com/michelp/pgsodium/#sealed-boxes)
|
||||
- [Authenticated encryption](https://github.com/michelp/pgsodium/#authenticated-encryption-1)
|
||||
- [Public key signatures](https://github.com/michelp/pgsodium/#public-key-signatures)
|
||||
- [Sealed boxes](https://github.com/michelp/pgsodium/#sealed-boxes)
|
||||
- [Hashing](https://github.com/michelp/pgsodium/#hashing)
|
||||
- [Password hashing](https://github.com/michelp/pgsodium/#password-hashing)
|
||||
- [Key Derivation](https://github.com/michelp/pgsodium/#key-derivation)
|
||||
|
||||
@@ -15,6 +15,7 @@ Even though indexes improve query performance, the Postgres query planner may no
|
||||
## Create an index
|
||||
|
||||
Let's take an example table:
|
||||
|
||||
```sql
|
||||
create table persons (
|
||||
id bigint generated by default as identity primary key,
|
||||
@@ -25,6 +26,7 @@ create table persons (
|
||||
deceased boolean
|
||||
);
|
||||
```
|
||||
|
||||
<Admonition>
|
||||
|
||||
All the queries in this guide can be run using the [SQL Editor](https://app.supabase.com/project/_/sql) in the Supabase Dashboard, or via `psql` if you're [connecting directly to the database](/docs/guides/database/connecting-to-postgres#direct-connections).
|
||||
@@ -32,6 +34,7 @@ All the queries in this guide can be run using the [SQL Editor](https://app.supa
|
||||
</Admonition>
|
||||
|
||||
We might want to frequently query users based on their age:
|
||||
|
||||
```sql
|
||||
select name from persons where age = 32;
|
||||
```
|
||||
@@ -39,19 +42,22 @@ select name from persons where age = 32;
|
||||
Without an index, Postgres will scan every row in the table to find equality matches on age.
|
||||
|
||||
You can verify this by doing an explain on the query:
|
||||
|
||||
```sql
|
||||
explain select name from persons where age = 32;
|
||||
```
|
||||
|
||||
Outputs:
|
||||
|
||||
```
|
||||
Seq Scan on persons (cost=0.00..22.75 rows=x width=y)
|
||||
Filter: (age = 32)
|
||||
```
|
||||
|
||||
To add a simple B-Tree index you can run:
|
||||
|
||||
```sql
|
||||
create index idx_persons_age on persons(age);
|
||||
create index idx_persons_age on persons (age);
|
||||
```
|
||||
|
||||
<Admonition type="caution">
|
||||
@@ -73,7 +79,8 @@ You can see that in any large data set, traversing the index to locate a given v
|
||||
If you are frequently querying a subset of rows then it may be more efficient to build a partial index. In our example, perhaps we only want to match on `age` where `deceased is false`. We could build a partial index:
|
||||
|
||||
```sql
|
||||
create index idx_living_persons_age ON persons(age) where deceased is false;
|
||||
create index idx_living_persons_age on persons (age)
|
||||
where deceased is false;
|
||||
```
|
||||
|
||||
## Ordering indexes
|
||||
@@ -81,14 +88,13 @@ create index idx_living_persons_age ON persons(age) where deceased is false;
|
||||
By default B-Tree indexes are sorted in ascending order, but sometimes you may want to provide a different ordering. Perhaps our application has a page featuring the top 10 oldest people. Here we would want to sort in descending order, and include `NULL` values last. For this we can use:
|
||||
|
||||
```sql
|
||||
create index idx_persons_age_desc on persons(age desc nulls last);
|
||||
create index idx_persons_age_desc on persons (age desc nulls last);
|
||||
```
|
||||
|
||||
## Reindexing
|
||||
|
||||
After a while indexes can become stale and may need rebuilding. Postgres provides a `reindex` command for this, but due to Postgres locks being placed on the index during this process, you may want to make use of the `concurrent` keyword.
|
||||
|
||||
|
||||
```sql
|
||||
reindex index concurrently idx_persons_age;
|
||||
```
|
||||
@@ -103,4 +109,4 @@ Take note that `reindex` can be used inside a transaction, but `reindex [index/t
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
export default Page
|
||||
Loaded 100 of 612 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user