mirror of
https://github.com/supabase/supabase.git
synced 2026-10-07 02:15:05 +03:00
207 files changed
+40176
-17725
No files matched your search
@@ -34,5 +34,8 @@ jobs:
|
||||
run: npm ci
|
||||
working-directory: ./
|
||||
- name: Run tests
|
||||
env:
|
||||
# Default is 2 GB, increase to have less frequent OOM errors
|
||||
NODE_OPTIONS: "--max_old_space_size=3072"
|
||||
run: npm run test:studio
|
||||
working-directory: ./
|
||||
@@ -122,5 +122,8 @@ typings/
|
||||
|
||||
apps/new-docs/*
|
||||
|
||||
# UI tokens
|
||||
packages/ui/tokens/**/*.json
|
||||
|
||||
# For self-hosted logs: https://github.com/supabase/supabase/blob/86e3ab20abfdb9c3e666334d3d2f8efeef9ccf2c/docker/docker-compose-logging.yml#L101
|
||||
gcloud.json
|
||||
@@ -1 +1 @@
|
||||
^./i18n
|
||||
^./i18n
|
||||
@@ -5,7 +5,7 @@ export type FooterHelpCalloutType = 'default' | 'postgres'
|
||||
const content = {
|
||||
default: {
|
||||
title: 'Need some help?',
|
||||
description: `Not to worry, our specialist engineers are here to help. Submit a support ticket through the [Dashboard](https://app.supabase.com/support/new).`,
|
||||
description: `Not to worry, our specialist engineers are here to help. Submit a support ticket through the [Dashboard](https://supabase.com/dashboard/support/new).`,
|
||||
},
|
||||
postgres: {
|
||||
title: 'Looking for Serverless Postgres?',
|
||||
|
||||
@@ -475,10 +475,9 @@ export const auth = {
|
||||
items: [
|
||||
{ name: 'Overview', url: '/guides/auth/auth-helpers' },
|
||||
{ name: 'Auth UI', url: '/guides/auth/auth-helpers/auth-ui' },
|
||||
{ name: 'Next.js (pages)', url: '/guides/auth/auth-helpers/nextjs' },
|
||||
{
|
||||
name: 'Next.js (app)',
|
||||
url: '/guides/auth/auth-helpers/nextjs-server-components',
|
||||
name: 'Next.js',
|
||||
url: '/guides/auth/auth-helpers/nextjs',
|
||||
},
|
||||
{ name: 'Remix', url: '/guides/auth/auth-helpers/remix' },
|
||||
{ name: 'SvelteKit', url: '/guides/auth/auth-helpers/sveltekit' },
|
||||
@@ -751,6 +750,7 @@ export const functions: NavMenuConstant = {
|
||||
items: [
|
||||
{ name: 'Dart Edge on Supabase', url: '/guides/functions/dart-edge' },
|
||||
{ name: 'Browserless.io', url: '/guides/functions/examples/screenshots' },
|
||||
{ name: 'Hugging Face', url: '/guides/functions/examples/huggingface-image-captioning' },
|
||||
{ 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' },
|
||||
|
||||
@@ -129,7 +129,7 @@ const TopNavBar: FC = () => {
|
||||
type="outline"
|
||||
as="a"
|
||||
// @ts-ignore
|
||||
href="https://app.supabase.com"
|
||||
href="https://supabase.com/dashboard"
|
||||
className="text-scale-1100 text-sm"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
|
||||
@@ -119,7 +119,7 @@ const TopNavBarRef: FC = () => {
|
||||
type="text"
|
||||
as="a"
|
||||
// @ts-ignore
|
||||
href="https://app.supabase.com"
|
||||
href="https://supabase.com/dashboard"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
import '../../../packages/ui/build/css/themes/light.css'
|
||||
import '../../../packages/ui/build/css/themes/dark.css'
|
||||
|
||||
import '../styles/ch.scss'
|
||||
import '../styles/main.scss?v=1.0.0'
|
||||
import '../styles/new-docs.scss'
|
||||
import '../styles/prism-okaidia.scss'
|
||||
|
||||
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { SessionContextProvider } from '@supabase/auth-helpers-react'
|
||||
import { AuthProvider, ThemeProvider, useTelemetryProps } from 'common'
|
||||
@@ -9,10 +17,6 @@ import Favicons from '~/components/Favicons'
|
||||
import SiteLayout from '~/layouts/SiteLayout'
|
||||
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'
|
||||
import '../styles/new-docs.scss'
|
||||
import '../styles/prism-okaidia.scss'
|
||||
|
||||
function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
const router = useRouter()
|
||||
|
||||
@@ -11,10 +11,10 @@ Supabase provides client libraries for the REST and Realtime APIs. Some librarie
|
||||
|
||||
## Official Libraries
|
||||
|
||||
| `Language` | `Source Code` | `Documentation` |
|
||||
| --------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------- |
|
||||
| Javascript/Typescript | [supabase-js](https://github.com/supabase/supabase-js) | [Docs](https://supabase.com/docs/reference/javascript/introduction) |
|
||||
| Dart/Flutter | [supabase-dart](https://github.com/supabase/supabase-dart) | [Docs](https://supabase.com/docs/reference/dart/introduction) |
|
||||
| `Language` | `Source Code` | `Documentation` |
|
||||
| --------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
||||
| Javascript/Typescript | [supabase-js](https://github.com/supabase/supabase-js) | [Docs](https://supabase.com/docs/reference/javascript/introduction) |
|
||||
| Dart/Flutter | [supabase-flutter](https://github.com/supabase/supabase-flutter/tree/main/packages/supabase_flutter) | [Docs](https://supabase.com/docs/reference/dart/introduction) |
|
||||
|
||||
## Community Libraries
|
||||
|
||||
|
||||
@@ -0,0 +1,847 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'nextjs-pages',
|
||||
title: 'Supabase Auth with Next.js Pages Directory',
|
||||
description:
|
||||
'Authentication helpers for Next.js API routes, middleware, and SSR in the Pages Directory.',
|
||||
sidebar_label: 'Next.js (pages)',
|
||||
}
|
||||
|
||||
This submodule provides convenience helpers for implementing user authentication in Next.js applications using the pages directory.
|
||||
|
||||
> Note: As of [Next.js 13.4](https://nextjs.org/blog/next-13-4), the App Router has reached stable status. This is now the recommended path for new Next.js app. Check out our guide on using [Auth Helpers with the Next.js App Directory](/docs/guides/auth/auth-helpers/nextjs).
|
||||
|
||||
## Install the Next.js helper library
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="npm"
|
||||
>
|
||||
<TabPanel id="npm" label="npm">
|
||||
|
||||
```sh
|
||||
npm install @supabase/auth-helpers-nextjs
|
||||
```
|
||||
|
||||
This library supports the following tooling versions:
|
||||
|
||||
- Node.js: `^10.13.0 || >=12.0.0`
|
||||
- Next.js: `>=10`
|
||||
|
||||
Additionally, install the **React Auth Helpers** for components and hooks that can be used across all React-based frameworks.
|
||||
|
||||
```sh
|
||||
npm install @supabase/auth-helpers-react
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="yarn" label="Yarn">
|
||||
|
||||
```sh
|
||||
yarn add @supabase/auth-helpers-nextjs
|
||||
```
|
||||
|
||||
This library supports the following tooling versions:
|
||||
|
||||
- Node.js: `^10.13.0 || >=12.0.0`
|
||||
- Next.js: `>=10`
|
||||
|
||||
Additionally, install the **React Auth Helpers** for components and hooks that can be used across all React-based frameworks.
|
||||
|
||||
```sh
|
||||
yarn add @supabase/auth-helpers-react
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Set up environment variables
|
||||
|
||||
Retrieve your project URL and anon key in your project's [API settings](https://app.supabase.com/project/_/settings/api) in the Dashboard to set up the following environment variables. For local development you can set them in a `.env.local` file. See an [example](https://github.com/supabase/auth-helpers/blob/main/examples/nextjs/.env.local.example).
|
||||
|
||||
```bash title=.env.local
|
||||
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
|
||||
```
|
||||
|
||||
## Basic Setup
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
Wrap your `pages/_app.js` component with the `SessionContextProvider` component:
|
||||
|
||||
```jsx title=pages/_app.js
|
||||
import { createPagesBrowserClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { SessionContextProvider } from '@supabase/auth-helpers-react'
|
||||
import { useState } from 'react'
|
||||
|
||||
function MyApp({ Component, pageProps }) {
|
||||
// Create a new supabase browser client on every first render.
|
||||
const [supabaseClient] = useState(() => createPagesBrowserClient())
|
||||
|
||||
return (
|
||||
<SessionContextProvider
|
||||
supabaseClient={supabaseClient}
|
||||
initialSession={pageProps.initialSession}
|
||||
>
|
||||
<Component {...pageProps} />
|
||||
</SessionContextProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component:
|
||||
|
||||
```tsx lines=2,8 title=pages/_app.tsx
|
||||
import { createPagesBrowserClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { SessionContextProvider, Session } from '@supabase/auth-helpers-react'
|
||||
import { useState } from 'react'
|
||||
|
||||
function MyApp({
|
||||
Component,
|
||||
pageProps,
|
||||
}: AppProps<{
|
||||
initialSession: Session
|
||||
}>) {
|
||||
// Create a new supabase browser client on every first render.
|
||||
const [supabaseClient] = useState(() => createPagesBrowserClient())
|
||||
|
||||
return (
|
||||
<SessionContextProvider
|
||||
supabaseClient={supabaseClient}
|
||||
initialSession={pageProps.initialSession}
|
||||
>
|
||||
<Component {...pageProps} />
|
||||
</SessionContextProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
You can now determine if a user is authenticated by checking that the `user` object returned by the `useUser()` hook is defined.
|
||||
|
||||
## Usage with TypeScript
|
||||
|
||||
You can pass types that were [generated with the Supabase CLI](/docs/reference/javascript/typescript-support#generating-types) to the Supabase Client to get enhanced type safety and auto completion:
|
||||
|
||||
### Browser client
|
||||
|
||||
Creating a new supabase client object:
|
||||
|
||||
```tsx
|
||||
import { createPagesBrowserClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { Database } from '../database.types'
|
||||
|
||||
const supabaseClient = createPagesBrowserClient<Database>()
|
||||
```
|
||||
|
||||
Retrieving a supabase client object from the SessionContext:
|
||||
|
||||
```tsx
|
||||
import { useSupabaseClient } from '@supabase/auth-helpers-react'
|
||||
import { Database } from '../database.types'
|
||||
|
||||
const supabaseClient = useSupabaseClient<Database>()
|
||||
```
|
||||
|
||||
### Server client
|
||||
|
||||
```tsx
|
||||
// Creating a new supabase server client object (e.g. in API route):
|
||||
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { Database } from 'types_db'
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const supabaseServerClient = createPagesServerClient<Database>({
|
||||
req,
|
||||
res,
|
||||
})
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabaseServerClient.auth.getUser()
|
||||
|
||||
res.status(200).json({ name: user?.name ?? '' })
|
||||
}
|
||||
```
|
||||
|
||||
## Client-side data fetching with RLS
|
||||
|
||||
For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to use the `supabaseClient` from the `useSupabaseClient` hook and only run your query once the user is defined client-side in the `useUser()` hook:
|
||||
|
||||
```jsx lines=10-17
|
||||
import { Auth } from '@supabase/auth-ui-react'
|
||||
import { ThemeSupa } from '@supabase/auth-ui-shared'
|
||||
import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
const LoginPage = () => {
|
||||
const supabaseClient = useSupabaseClient()
|
||||
const user = useUser()
|
||||
const [data, setData] = useState()
|
||||
|
||||
useEffect(() => {
|
||||
async function loadData() {
|
||||
const { data } = await supabaseClient.from('test').select('*')
|
||||
setData(data)
|
||||
}
|
||||
// Only run query once user is logged in.
|
||||
if (user) loadData()
|
||||
}, [user])
|
||||
|
||||
if (!user)
|
||||
return (
|
||||
<Auth
|
||||
redirectTo="http://localhost:3000/"
|
||||
appearance={{ theme: ThemeSupa }}
|
||||
supabaseClient={supabaseClient}
|
||||
providers={['google', 'github']}
|
||||
socialLayout="horizontal"
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => supabaseClient.auth.signOut()}>Sign out</button>
|
||||
<p>user:</p>
|
||||
<pre>{JSON.stringify(user, null, 2)}</pre>
|
||||
<p>client-side data fetching with RLS</p>
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginPage
|
||||
```
|
||||
|
||||
## Server-side rendering (SSR)
|
||||
|
||||
Create a server supabase client to retrieve the logged in user's session:
|
||||
|
||||
```jsx title=pages/profile.js
|
||||
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
export default function Profile({ user }) {
|
||||
return <div>Hello {user.name}</div>
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (ctx) => {
|
||||
// Create authenticated Supabase Client
|
||||
const supabase = createPagesServerClient(ctx)
|
||||
// Check if we have a session
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
if (!session)
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/',
|
||||
permanent: false,
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
initialSession: session,
|
||||
user: session.user,
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Server-side data fetching with RLS
|
||||
|
||||
You can use the server supabase client to run [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) authenticated queries server-side:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```jsx
|
||||
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
export default function ProtectedPage({ user, data }) {
|
||||
return (
|
||||
<>
|
||||
<div>Protected content for {user.email}</div>
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
<pre>{JSON.stringify(user, null, 2)}</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (ctx) => {
|
||||
// Create authenticated Supabase Client
|
||||
const supabase = createPagesServerClient(ctx)
|
||||
// Check if we have a session
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
if (!session)
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/',
|
||||
permanent: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Run queries with RLS on the server
|
||||
const { data } = await supabase.from('users').select('*')
|
||||
|
||||
return {
|
||||
props: {
|
||||
initialSession: session,
|
||||
user: session.user,
|
||||
data: data ?? [],
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
```tsx
|
||||
import { User, createPagesServerClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { GetServerSidePropsContext } from 'next'
|
||||
|
||||
export default function ProtectedPage({ user, data }: { user: User; data: any }) {
|
||||
return (
|
||||
<>
|
||||
<div>Protected content for {user.email}</div>
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
<pre>{JSON.stringify(user, null, 2)}</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
|
||||
// Create authenticated Supabase Client
|
||||
const supabase = createPagesServerClient(ctx)
|
||||
// Check if we have a session
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
if (!session)
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/',
|
||||
permanent: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Run queries with RLS on the server
|
||||
const { data } = await supabase.from('users').select('*')
|
||||
|
||||
return {
|
||||
props: {
|
||||
initialSession: session,
|
||||
user: session.user,
|
||||
data: data ?? [],
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Server-side data fetching to OAuth APIs using `provider token` {`#oauth-provider-token`}
|
||||
|
||||
When using third-party auth providers, sessions are initiated with an additional `provider_token` field which is persisted in the auth cookie and can be accessed within the session object. The `provider_token` can be used to make API requests to the OAuth provider's API endpoints on behalf of the logged-in user.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```jsx
|
||||
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
export default function ProtectedPage({ user, allRepos }) {
|
||||
return (
|
||||
<>
|
||||
<div>Protected content for {user.email}</div>
|
||||
<p>Data fetched with provider token:</p>
|
||||
<pre>{JSON.stringify(allRepos, null, 2)}</pre>
|
||||
<p>user:</p>
|
||||
<pre>{JSON.stringify(user, null, 2)}</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (ctx) => {
|
||||
// Create authenticated Supabase Client
|
||||
const supabase = createPagesServerClient(ctx)
|
||||
// Check if we have a session
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
if (!session)
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/',
|
||||
permanent: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Retrieve provider_token & logged in user's third-party id from metadata
|
||||
const { provider_token, user } = session
|
||||
const userId = user.user_metadata.user_name
|
||||
|
||||
const allRepos = await (
|
||||
await fetch(`https://api.github.com/search/repositories?q=user:${userId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `token ${provider_token}`,
|
||||
},
|
||||
})
|
||||
).json()
|
||||
|
||||
return { props: { user, allRepos } }
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
```tsx
|
||||
import { User, createPagesServerClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { GetServerSidePropsContext } from 'next'
|
||||
|
||||
export default function ProtectedPage({ user, allRepos }: { user: User; allRepos: any }) {
|
||||
return (
|
||||
<>
|
||||
<div>Protected content for {user.email}</div>
|
||||
<p>Data fetched with provider token:</p>
|
||||
<pre>{JSON.stringify(allRepos, null, 2)}</pre>
|
||||
<p>user:</p>
|
||||
<pre>{JSON.stringify(user, null, 2)}</pre>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
|
||||
// Create authenticated Supabase Client
|
||||
const supabase = createPagesServerClient(ctx)
|
||||
// Check if we have a session
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
if (!session)
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/',
|
||||
permanent: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Retrieve provider_token & logged in user's third-party id from metadata
|
||||
const { provider_token, user } = session
|
||||
const userId = user.user_metadata.user_name
|
||||
|
||||
const allRepos = await (
|
||||
await fetch(`https://api.github.com/search/repositories?q=user:${userId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `token ${provider_token}`,
|
||||
},
|
||||
})
|
||||
).json()
|
||||
|
||||
return { props: { user, allRepos } }
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Protecting API routes
|
||||
|
||||
Create a server supabase client to retrieve the logged in user's session:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```jsx title=pages/api/protected-route.js
|
||||
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
const ProtectedRoute = async (req, res) => {
|
||||
// Create authenticated Supabase Client
|
||||
const supabase = createPagesServerClient({ req, res })
|
||||
// Check if we have a session
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
if (!session)
|
||||
return res.status(401).json({
|
||||
error: 'not_authenticated',
|
||||
description: 'The user does not have an active session or is not authenticated',
|
||||
})
|
||||
|
||||
// Run queries with RLS on the server
|
||||
const { data } = await supabase.from('test').select('*')
|
||||
res.json(data)
|
||||
}
|
||||
|
||||
export default ProtectedRoute
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
```tsx title=pages/api/protected-route.ts
|
||||
import { NextApiHandler } from 'next'
|
||||
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
const ProtectedRoute: NextApiHandler = async (req, res) => {
|
||||
// Create authenticated Supabase Client
|
||||
const supabase = createPagesServerClient({ req, res })
|
||||
// Check if we have a session
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
if (!session)
|
||||
return res.status(401).json({
|
||||
error: 'not_authenticated',
|
||||
description: 'The user does not have an active session or is not authenticated',
|
||||
})
|
||||
|
||||
// Run queries with RLS on the server
|
||||
const { data } = await supabase.from('test').select('*')
|
||||
res.json(data)
|
||||
}
|
||||
|
||||
export default ProtectedRoute
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Auth with Next.js Middleware
|
||||
|
||||
As an alternative to protecting individual pages you can use a [Next.js Middleware](https://nextjs.org/docs/middleware) to protect the entire directory or those that match the config object. In the following example, all requests to `/middleware-protected/*` will check whether a user is signed in, if successful the request will be forwarded to the destination route, otherwise the user will be redirected:
|
||||
|
||||
```ts title=middleware.ts
|
||||
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { NextResponse } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
|
||||
export async function middleware(req: NextRequest) {
|
||||
// We need to create a response and hand it to the supabase client to be able to modify the response headers.
|
||||
const res = NextResponse.next()
|
||||
// Create authenticated Supabase Client.
|
||||
const supabase = createMiddlewareClient({ req, res })
|
||||
// Check if we have a session
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
// Check auth condition
|
||||
if (session?.user.email?.endsWith('@gmail.com')) {
|
||||
// Authentication successful, forward request to protected route.
|
||||
return res
|
||||
}
|
||||
|
||||
// Auth condition not met, redirect to home page.
|
||||
const redirectUrl = req.nextUrl.clone()
|
||||
redirectUrl.pathname = '/'
|
||||
redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
|
||||
return NextResponse.redirect(redirectUrl)
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: '/middleware-protected/:path*',
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Migrating to v0.5.X
|
||||
|
||||
To make these helpers more flexible as well as more maintainable and easier to upgrade for new versions of Next.js, we're stripping them down to the most useful part which is managing the cookies and giving you an authenticated supabase-js client in any environment (client, server, middleware/edge).
|
||||
|
||||
Therefore we're marking the `withApiAuth`, `withPageAuth`, and `withMiddlewareAuth` higher order functions as deprecated and they will be removed in the next **minor** release (v0.6.X).
|
||||
|
||||
Please follow the steps below to update your API routes, pages, and middleware handlers. Thanks!
|
||||
|
||||
#### `withApiAuth` deprecated!
|
||||
|
||||
Use `createPagesServerClient` within your `NextApiHandler`:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="before"
|
||||
>
|
||||
<TabPanel id="before" label="Before">
|
||||
|
||||
```tsx title=pages/api/protected-route.ts
|
||||
import { withApiAuth } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
export default withApiAuth(async function ProtectedRoute(req, res, supabase) {
|
||||
// Run queries with RLS on the server
|
||||
const { data } = await supabase.from('test').select('*')
|
||||
res.json(data)
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="after" label="After">
|
||||
|
||||
```tsx title=pages/api/protected-route.ts
|
||||
import { NextApiHandler } from 'next'
|
||||
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
const ProtectedRoute: NextApiHandler = async (req, res) => {
|
||||
// Create authenticated Supabase Client
|
||||
const supabase = createPagesServerClient({ req, res })
|
||||
// Check if we have a session
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
if (!session)
|
||||
return res.status(401).json({
|
||||
error: 'not_authenticated',
|
||||
description: 'The user does not have an active session or is not authenticated',
|
||||
})
|
||||
|
||||
// Run queries with RLS on the server
|
||||
const { data } = await supabase.from('test').select('*')
|
||||
res.json(data)
|
||||
}
|
||||
|
||||
export default ProtectedRoute
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
#### `withPageAuth` deprecated!
|
||||
|
||||
Use `createPagesServerClient` within `getServerSideProps`:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="before"
|
||||
>
|
||||
<TabPanel id="before" label="Before">
|
||||
|
||||
```tsx title=pages/profile.tsx
|
||||
import { withPageAuth, User } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
export default function Profile({ user }: { user: User }) {
|
||||
return <pre>{JSON.stringify(user, null, 2)}</pre>
|
||||
}
|
||||
|
||||
export const getServerSideProps = withPageAuth({ redirectTo: '/' })
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="after" label="After">
|
||||
|
||||
```tsx title=pages/profile.js
|
||||
import { createPagesServerClient, User } from '@supabase/auth-helpers-nextjs'
|
||||
import { GetServerSidePropsContext } from 'next'
|
||||
|
||||
export default function Profile({ user }: { user: User }) {
|
||||
return <pre>{JSON.stringify(user, null, 2)}</pre>
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
|
||||
// Create authenticated Supabase Client
|
||||
const supabase = createPagesServerClient(ctx)
|
||||
// Check if we have a session
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
if (!session)
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/',
|
||||
permanent: false,
|
||||
},
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
initialSession: session,
|
||||
user: session.user,
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
#### `withMiddlewareAuth` deprecated!
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="before"
|
||||
>
|
||||
<TabPanel id="before" label="Before">
|
||||
|
||||
```tsx title=middleware.ts
|
||||
import { withMiddlewareAuth } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
export const middleware = withMiddlewareAuth({
|
||||
redirectTo: '/',
|
||||
authGuard: {
|
||||
isPermitted: async (user) => {
|
||||
return user.email?.endsWith('@gmail.com') ?? false
|
||||
},
|
||||
redirectTo: '/insufficient-permissions',
|
||||
},
|
||||
})
|
||||
|
||||
export const config = {
|
||||
matcher: '/middleware-protected',
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="after" label="After">
|
||||
|
||||
```tsx title=middleware.ts
|
||||
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { NextResponse } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
|
||||
export async function middleware(req: NextRequest) {
|
||||
// We need to create a response and hand it to the supabase client to be able to modify the response headers.
|
||||
const res = NextResponse.next()
|
||||
// Create authenticated Supabase Client.
|
||||
const supabase = createMiddlewareClient({ req, res })
|
||||
// Check if we have a session
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
// Check auth condition
|
||||
if (session?.user.email?.endsWith('@gmail.com')) {
|
||||
// Authentication successful, forward request to protected route.
|
||||
return res
|
||||
}
|
||||
|
||||
// Auth condition not met, redirect to home page.
|
||||
const redirectUrl = req.nextUrl.clone()
|
||||
redirectUrl.pathname = '/'
|
||||
redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
|
||||
return NextResponse.redirect(redirectUrl)
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: '/middleware-protected',
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Migrating to v0.4.X and supabase-js v2
|
||||
|
||||
With the update to `supabase-js` v2 the `auth` API routes are no longer required, therefore you can go ahead and delete your `auth` directory under the `/pages/api/` directory. Please refer to the [v2 migration guide](/docs/reference/javascript/v1/upgrade-guide) for the full set of changes within supabase-js.
|
||||
|
||||
The `/api/auth/logout` API route has been removed, please use the `signout` method instead:
|
||||
|
||||
```jsx
|
||||
<button
|
||||
onClick={async () => {
|
||||
await supabaseClient.auth.signOut()
|
||||
router.push('/')
|
||||
}}
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
```
|
||||
|
||||
The `supabaseClient` and `supabaseServerClient` have been removed in favor of the `createPagesBrowserClient` and `createPagesServerClient` methods. This allows you to provide the CLI-generated types to the client:
|
||||
|
||||
```tsx
|
||||
// client-side
|
||||
import type { Database } from 'types_db'
|
||||
const [supabaseClient] = useState(() => createPagesBrowserClient<Database>())
|
||||
|
||||
// server-side API route
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { Database } from 'types_db'
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const supabaseServerClient = createPagesServerClient<Database>({
|
||||
req,
|
||||
res,
|
||||
})
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabaseServerClient.auth.getUser()
|
||||
|
||||
res.status(200).json({ name: user?.name ?? '' })
|
||||
}
|
||||
```
|
||||
|
||||
- The `UserProvider` has been replaced by the `SessionContextProvider`. Make sure to wrap your `pages/_app.js` componenent with the `SessionContextProvider`. Then, throughout your application you can use the `useSessionContext` hook to get the `session` and the `useSupabaseClient` hook to get an authenticated `supabaseClient`.
|
||||
- The `useUser` hook now returns the `user` object or `null`.
|
||||
- Usage with TypeScript: You can pass types that were [generated with the Supabase CLI](/docs/reference/javascript/typescript-support#generating-types) to the Supabase Client to get enhanced type safety and auto completion:
|
||||
|
||||
Creating a new supabase client object:
|
||||
|
||||
```tsx
|
||||
import { Database } from '../database.types'
|
||||
|
||||
const [supabaseClient] = useState(() => createPagesBrowserClient<Database>())
|
||||
```
|
||||
|
||||
Retrieving a supabase client object from the SessionContext:
|
||||
|
||||
```tsx
|
||||
import { useSupabaseClient } from '@supabase/auth-helpers-react'
|
||||
import { Database } from '../database.types'
|
||||
|
||||
const supabaseClient = useSupabaseClient<Database>()
|
||||
```
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -1,516 +0,0 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'nextjs-server-components',
|
||||
title: 'Supabase Auth with Next.js app directory',
|
||||
description:
|
||||
'Authentication helpers for creating an authenticated Supabase client in Next.js 13 app directory Server Components and Route Handlers.',
|
||||
sidebar_label: 'Next.js (app)',
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
> 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
|
||||
src="https://www.youtube-nocookie.com/embed/Bh1TOpOcGJQ"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
> To learn more about Supabase and the Next.js 13 app directory, check out [this playlist](https://youtube.com/playlist?list=PL5S4mPUpp4OtwG-qCxm8gA_hjaBq0OPdz).
|
||||
|
||||
## Install the Next.js helper library
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="npm"
|
||||
>
|
||||
|
||||
<TabPanel id="npm" label="npm">
|
||||
|
||||
```sh
|
||||
npm install @supabase/auth-helpers-nextjs
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="yarn" label="Yarn">
|
||||
|
||||
```sh
|
||||
yarn add @supabase/auth-helpers-nextjs
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Set up environment variables
|
||||
|
||||
Retrieve your project's URL and anon key from your [API settings](https://app.supabase.com/project/_/settings/api) in the dashboard, and create a `.env.local` file with the following environment variables:
|
||||
|
||||
```bash title=".env.local"
|
||||
NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
|
||||
```
|
||||
|
||||
## Configure Middleware
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
Middleware runs immediately before each route in rendered. Next.js only provides read access to headers and cookies in Server Components and Route Handlers, however, Supabase needs to be able to set cookies and headers to refresh expired access tokens. Therefore, you must call the `getSession` function in `middleware.js` in order to use a Supabase client in Server Components or Route Handlers.
|
||||
|
||||
Create a new `middleware.js` file in the root of your project and populate with the following:
|
||||
|
||||
```jsx title="middleware.js"
|
||||
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function middleware(req) {
|
||||
const res = NextResponse.next()
|
||||
const supabase = createMiddlewareSupabaseClient({ req, res })
|
||||
await supabase.auth.getSession()
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
Middleware runs immediately before each route in rendered. Next.js only provides read access to headers and cookies in Server Components and Route Handlers, however, Supabase needs to be able to set cookies and headers to refresh expired access tokens. Therefore, you must call the `getSession` function in `middleware.ts` in order to use a Supabase client in Server Components or Route Handlers.
|
||||
|
||||
Create a new `middleware.ts` file in the root of your project and populate with the following:
|
||||
|
||||
```tsx title="middleware.ts"
|
||||
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
import type { NextRequest } from 'next/server'
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
export async function middleware(req: NextRequest) {
|
||||
const res = NextResponse.next()
|
||||
const supabase = createMiddlewareSupabaseClient<Database>({ req, res })
|
||||
await supabase.auth.getSession()
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createMiddlewareSupabaseClient` to add type support to the Supabase client.
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Supabase Provider
|
||||
|
||||
All Client Components need to share a single instance of the Supabase client. We can wrap our application in a `<SupabaseProvider />` and use React Context to create a global Supabase instance.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
Create a new file at `/app/supabase-provider.jsx` and populate with the following:
|
||||
|
||||
```jsx title="app/supabase-provider.jsx"
|
||||
'use client'
|
||||
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
const Context = createContext(undefined)
|
||||
|
||||
export default function SupabaseProvider({ children, session }) {
|
||||
const [supabase] = useState(() => createBrowserSupabaseClient())
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
const {
|
||||
data: { subscription },
|
||||
} = supabase.auth.onAuthStateChange(() => {
|
||||
router.refresh()
|
||||
})
|
||||
|
||||
return () => {
|
||||
subscription.unsubscribe()
|
||||
}
|
||||
}, [router, supabase])
|
||||
|
||||
return (
|
||||
<Context.Provider value={{ supabase, session }}>
|
||||
<>{children}</>
|
||||
</Context.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useSupabase = () => {
|
||||
const context = useContext(Context)
|
||||
|
||||
if (context === undefined) {
|
||||
throw new Error('useSupabase must be used inside SupabaseProvider')
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
Create a new file at `/app/supabase-provider.tsx` and populate with the following:
|
||||
|
||||
```tsx title="app/supabase-provider.tsx"
|
||||
'use client'
|
||||
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
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,
|
||||
session,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
session: MaybeSession
|
||||
}) {
|
||||
const [supabase] = useState(() => createBrowserSupabaseClient())
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
const {
|
||||
data: { subscription },
|
||||
} = supabase.auth.onAuthStateChange(() => {
|
||||
router.refresh()
|
||||
})
|
||||
|
||||
return () => {
|
||||
subscription.unsubscribe()
|
||||
}
|
||||
}, [router, supabase])
|
||||
|
||||
return (
|
||||
<Context.Provider value={{ supabase, session }}>
|
||||
<>{children}</>
|
||||
</Context.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useSupabase = () => {
|
||||
const context = useContext(Context)
|
||||
|
||||
if (context === undefined) {
|
||||
throw new Error('useSupabase must be used inside SupabaseProvider')
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
```
|
||||
|
||||
> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createBrowserSupabaseClient` to add type support to the Supabase client.
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
Modify `layout.jsx` to wrap the application with the `<SupabaseProvider>` component:
|
||||
|
||||
```jsx title="app/layout.jsx"
|
||||
import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { headers, cookies } from 'next/headers'
|
||||
import './globals.css'
|
||||
import SupabaseProvider from './supabase-provider'
|
||||
|
||||
export const metadata = {
|
||||
title: 'Create Next App',
|
||||
description: 'Generated by create next app',
|
||||
}
|
||||
|
||||
export default async function RootLayout({ children }) {
|
||||
const supabase = createServerComponentSupabaseClient({
|
||||
headers,
|
||||
cookies,
|
||||
})
|
||||
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<SupabaseProvider session={session}>{children}</SupabaseProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
Modify `layout.tsx` to wrap the application with the `<SupabaseProvider>` component:
|
||||
|
||||
```tsx title="app/layout.tsx"
|
||||
import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { headers, cookies } from 'next/headers'
|
||||
import './globals.css'
|
||||
import SupabaseProvider from './supabase-provider'
|
||||
|
||||
export const metadata = {
|
||||
title: 'Create Next App',
|
||||
description: 'Generated by create next app',
|
||||
}
|
||||
|
||||
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
const supabase = createServerComponentSupabaseClient({
|
||||
headers,
|
||||
cookies,
|
||||
})
|
||||
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<SupabaseProvider session={session}>{children}</SupabaseProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
Now any of our Client Components can use the `useSupabase` hook to ensure they are using the same instance of a Supabase client.
|
||||
|
||||
## Creating a Supabase Client
|
||||
|
||||
### Client Components
|
||||
|
||||
While Server Components are great for data fetching, we still need to use Supabase client-side for [authentication](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/login.tsx) and [realtime subscriptions](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/realtime-posts.tsx).
|
||||
|
||||
As mentioned above, it is important that all Client Components share a single instance of the Supabase client. We can use the `useSupabase` hook we created above to ensure this is the case.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```jsx title="app/new-post.jsx"
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useSupabase } from './supabase-provider'
|
||||
|
||||
export default function NewPost() {
|
||||
const [content, setContent] = useState('')
|
||||
const { supabase } = useSupabase()
|
||||
|
||||
const handleSave = async () => {
|
||||
const { data } = await supabase.from('posts').insert({ content }).select()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<input onChange={(e) => setContent(e.target.value)} value={content} />
|
||||
<button onClick={handleSave}>Save</button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
```jsx title="app/new-post.tsx"
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useSupabase } from './supabase-provider'
|
||||
|
||||
export default function NewPost() {
|
||||
const [content, setContent] = useState('')
|
||||
const { supabase } = useSupabase()
|
||||
|
||||
const handleSave = async () => {
|
||||
const { data } = await supabase.from('posts').insert({ content }).select()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<input onChange={(e) => setContent(e.target.value)} value={content} />
|
||||
<button onClick={handleSave}>Save</button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
> check out [this example](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/supabase-provider.tsx) for making the user's session available to all Client Components.
|
||||
|
||||
### Server Components
|
||||
|
||||
In order to use Supabase in Server Components, you need to have implemented the `middleware.ts` steps above 👆
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```jsx title="app/page.jsx"
|
||||
import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { headers, cookies } from 'next/headers'
|
||||
|
||||
// do not cache this page
|
||||
export const revalidate = 0
|
||||
|
||||
export default async function ServerComponent() {
|
||||
const supabase = createServerComponentSupabaseClient({
|
||||
headers,
|
||||
cookies,
|
||||
})
|
||||
const { data } = await supabase.from('posts').select('*')
|
||||
|
||||
return <pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
```tsx title="app/page.tsx"
|
||||
import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { headers, cookies } from 'next/headers'
|
||||
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
// do not cache this page
|
||||
export const revalidate = 0
|
||||
|
||||
export default async function ServerComponent() {
|
||||
const supabase = createServerComponentSupabaseClient<Database>({
|
||||
headers,
|
||||
cookies,
|
||||
})
|
||||
const { data } = await supabase.from('posts').select('*')
|
||||
|
||||
return <pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
> check out [this example](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/page.tsx) for redirecting unauthenticated users - protected pages.
|
||||
|
||||
### Route Handlers
|
||||
|
||||
In order to use Supabase in Route Handlers, you need to have implemented the `middleware.ts` steps above 👆
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="js"
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
```jsx title="app/api/posts/route.jsx"
|
||||
import { createRouteHandlerSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { headers, cookies } from 'next/headers'
|
||||
|
||||
// do not cache this page
|
||||
export const revalidate = 0
|
||||
|
||||
export async function GET() {
|
||||
const supabase = createRouteHandlerSupabaseClient({
|
||||
headers,
|
||||
cookies,
|
||||
})
|
||||
const { data } = await supabase.from('posts').select('*')
|
||||
return NextResponse.json(data)
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
```tsx title="app/api/posts/route.tsx"
|
||||
import { createRouteHandlerSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { headers, cookies } from 'next/headers'
|
||||
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
// do not cache this page
|
||||
export const revalidate = 0
|
||||
|
||||
export async function GET() {
|
||||
const supabase = createRouteHandlerSupabaseClient<Database>({
|
||||
headers,
|
||||
cookies,
|
||||
})
|
||||
const { data } = await supabase.from('posts').select('*')
|
||||
|
||||
return NextResponse.json(data)
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
> Check out [this repo](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs) for a full example including [authentication](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/login.tsx), [realtime](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/realtime-posts.tsx) and [protected pages](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/page.tsx).
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
File diff suppressed because it is too large.
Load diff
@@ -113,6 +113,11 @@ export const examples = [
|
||||
description: `Request Routing with Oak server middleware.`,
|
||||
href: 'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/oak-server',
|
||||
},
|
||||
{
|
||||
name: 'Hugging Face',
|
||||
description: `Access 100,000+ Machine Learning models.`,
|
||||
href: '/guides/functions/examples/huggingface-image-captioning',
|
||||
},
|
||||
{
|
||||
name: 'OpenAI',
|
||||
description: `Using OpenAI in Edge Functions.`,
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
title: 'Hugging Face',
|
||||
description:
|
||||
'Use the Hugging Face Inference API to make calls to 100,000+ Machine Learning models from Supabase Edge Functions.',
|
||||
video: 'https://www.youtube.com/v/OgnYxRkxEUw',
|
||||
tocVideo: 'OgnYxRkxEUw',
|
||||
}
|
||||
|
||||
[Hugging Face](https://huggingface.co/) is the collaboration platform for the machine learning community.
|
||||
|
||||
[Huggingface.js](https://huggingface.co/docs/huggingface.js/index) provides a convenient way to make calls to 100,000+ Machine Learning models, making it easy to incorporate AI functionality into your [Supabase Edge Functions](https://supabase.com/edge-functions).
|
||||
|
||||
Putting this together with [Supabase Storage](https://supabase.com/storage) and [Database Webhooks](https://supabase.com/docs/guides/database/webhooks) we can easily put together a service that automatically generates captions for any image we upload to a storage bucket.
|
||||
|
||||
<div class="video-container">
|
||||
<iframe
|
||||
src="https://www.youtube-nocookie.com/embed/OgnYxRkxEUw"
|
||||
frameBorder="1"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
## Setup
|
||||
|
||||
- Open your Supabase project dashboard or [create a new project](https://app.supabase.com/projects).
|
||||
- [Create a new bucket](https://app.supabase.com/project/_/storage/buckets) called `images`.
|
||||
- Generate TypeScript types from remote Database.
|
||||
- Create a new Database table called `image_caption`.
|
||||
- Create `id` column of type `uuid` which references `storage.objects.id`.
|
||||
- Create a `caption` column of type `text`.
|
||||
- Regenerate TypeScript types to include new `image_caption` table.
|
||||
- Deploy the function to Supabase: `supabase functions deploy huggingface-image-captioning`.
|
||||
- Create the Database Webhook in the [Supabase Dashboard](https://app.supabase.com/project/_/database/hooks) to trigger the `huggingface-image-captioning` function anytime a record is added to the `storage.objects` table.
|
||||
|
||||
## Generate TypeScript Types
|
||||
|
||||
To generate the types.ts file for the storage and public schemas, run the following command in the terminal:
|
||||
|
||||
```bash
|
||||
supabase gen types typescript --project-id=your-project-ref --schema=storage,public > supabase/functions/huggingface-image-captioning/types.ts
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
Find the complete code on [GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/huggingface-image-captioning).
|
||||
|
||||
```ts
|
||||
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
|
||||
import { HfInference } from 'https://esm.sh/@huggingface/inference@2.3.2'
|
||||
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1'
|
||||
import { Database } from './types.ts'
|
||||
|
||||
console.log('Hello from `huggingface-image-captioning` function!')
|
||||
|
||||
const hf = new HfInference(Deno.env.get('HUGGINGFACE_ACCESS_TOKEN'))
|
||||
|
||||
type SoRecord = Database['storage']['Tables']['objects']['Row']
|
||||
interface WebhookPayload {
|
||||
type: 'INSERT' | 'UPDATE' | 'DELETE'
|
||||
table: string
|
||||
record: SoRecord
|
||||
schema: 'public'
|
||||
old_record: null | SoRecord
|
||||
}
|
||||
|
||||
serve(async (req) => {
|
||||
const payload: WebhookPayload = await req.json()
|
||||
const soRecord = payload.record
|
||||
const supabaseAdminClient = createClient<Database>(
|
||||
// Supabase API URL - env var exported by default when deployed.
|
||||
Deno.env.get('SUPABASE_URL') ?? '',
|
||||
// Supabase API SERVICE ROLE KEY - env var exported by default when deployed.
|
||||
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
|
||||
)
|
||||
|
||||
// Construct image url from storage
|
||||
const { data, error } = await supabaseAdminClient.storage
|
||||
.from(soRecord.bucket_id!)
|
||||
.createSignedUrl(soRecord.path_tokens!.join('/'), 60)
|
||||
if (error) throw error
|
||||
const { signedUrl } = data
|
||||
|
||||
// Run image captioning with Huggingface
|
||||
const imgDesc = await hf.imageToText({
|
||||
data: await (await fetch(signedUrl)).blob(),
|
||||
model: 'nlpconnect/vit-gpt2-image-captioning',
|
||||
})
|
||||
|
||||
// Store image caption in Database table
|
||||
await supabaseAdminClient
|
||||
.from('image_caption')
|
||||
.insert({ id: soRecord.id!, caption: imgDesc.generated_text })
|
||||
.throwOnError()
|
||||
|
||||
return new Response('ok')
|
||||
})
|
||||
```
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -34,9 +34,6 @@ Follow along in the video below as the founder of Snaplet, Peter Pistorius, take
|
||||
|
||||
> To create a read-only role across all schemas you can checkout the Snaplet [docs](https://docs.snaplet.dev/guides/postgresql/#create-a-read-only-role)
|
||||
|
||||
4. **Superuser access** for the development project. This can be done in Supabase via `Organization > SQL Editor > + New Query` and running this statement:
|
||||
`ALTER USER postgres WITH superuser;`
|
||||
|
||||
## Step 2: Copying your production database
|
||||
|
||||
### 2.1. Connect your data source
|
||||
@@ -84,23 +81,14 @@ Your data target is where you want Snaplet to restore the captured snapshot of y
|
||||
|
||||
> Remember the password you use when creating the project. You’ll need this password to connect your database to Snaplet later.
|
||||
|
||||
### 3.2. Make your `postgres` user a superuser
|
||||
|
||||
Snaplet requires the ability to drop the database schemas whilst restoring a snapshot. In order to do that, Snaplet requires `superuser` privileges.
|
||||
|
||||
1. Navigate to the `SQL Editor` in your Supabase console
|
||||
2. Click on `new query`
|
||||
3. Paste `alter user postgres with superuser;` into the SQL editor
|
||||
4. Run the query
|
||||
|
||||
### 3.3. Install the Snaplet CLI
|
||||
### 3.2. Install the Snaplet CLI
|
||||
|
||||
1. Open your terminal and run `curl -sL https://app.snaplet.dev/get-cli/ | bash`
|
||||
2. Run `snaplet auth`
|
||||
3. Navigate to [https://app.snaplet.dev/access-token/cli](https://app.snaplet.dev/access-token/cli) to get your access token
|
||||
4. Paste your access token in the terminal
|
||||
|
||||
### 3.4. Restore to the data target
|
||||
### 3.3. Restore to the data target
|
||||
|
||||
You're now ready to restore your production snapshot into your Supabase development project.
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ Chase Granberry
|
||||
Chris Copplestone
|
||||
Dave Wilson
|
||||
Div Arora
|
||||
Divit D
|
||||
Egor Romanov
|
||||
Fabrizio Fenoglio
|
||||
Francesco Sansalvadore
|
||||
|
||||
@@ -835,13 +835,13 @@
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/nextjs-server-components</loc>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/nextjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/nextjs</loc>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/nextjs-pages</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-scale-200 dark:bg-[#1c1c1c] #{!important};
|
||||
@apply bg-scale-200;
|
||||
scroll-behavior: smooth;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
@@ -1 +1,24 @@
|
||||
module.exports = require('config/tailwind.config')
|
||||
const config = require('config/tailwind.config')
|
||||
|
||||
module.exports = config({
|
||||
content: [
|
||||
'./../../packages/common/**/*.{ts,tsx}',
|
||||
'./../../packages/ui/**/*.{tsx,ts,js}',
|
||||
'./pages/**/*.{tsx,mdx}',
|
||||
'./components/**/*.tsx',
|
||||
'./layouts/**/*.tsx',
|
||||
'./src/**/*.{ts,tsx,mdx}',
|
||||
'./docs/**/*.{tsx,mdx}',
|
||||
],
|
||||
plugins: [
|
||||
function ({ addUtilities, addVariant }) {
|
||||
addUtilities({
|
||||
// prose (tailwind typography) helpers
|
||||
// useful for removing margins in prose styled sections
|
||||
'.prose--remove-p-margin p': {
|
||||
margin: '0',
|
||||
},
|
||||
})
|
||||
},
|
||||
],
|
||||
})
|
||||
@@ -105,14 +105,14 @@ Today the Community are releasing both the Flutter and Dart libraries in Beta (w
|
||||
|
||||
- Check out the [Flutter Quickstart Guide](/docs/guides/with-flutter)
|
||||
- Check out the code:
|
||||
- Supabase Flutter: [GitHub](https://github.com/supabase/supabase-flutter/) | [Release](https://pub.dev/packages/supabase_flutter)
|
||||
- Supabase Dart: [GitHub](https://github.com/supabase/supabase-dart/) | [Release](https://pub.dev/packages/supabase)
|
||||
- Supabase Flutter: [GitHub](https://github.com/supabase/supabase-flutter/tree/main/packages/supabase_flutter) | [Release](https://pub.dev/packages/supabase_flutter)
|
||||
- Supabase Dart: [GitHub](https://github.com/supabase/supabase-flutter/tree/main/packages/supabase) | [Release](https://pub.dev/packages/supabase)
|
||||
|
||||
### Get involved
|
||||
|
||||
- Help [write](/supasquad#author) the Flutter Docs.
|
||||
- Help [maintain](/supasquad#maintainer) the [Flutter](https://github.com/supabase/supabase-flutter/)
|
||||
and [Dart](https://github.com/supabase/supabase-dart/) libraries.
|
||||
- Help [maintain](/supasquad#maintainer) the [Flutter](https://github.com/supabase/supabase-flutter/tree/main/packages/supabase_flutter)
|
||||
and [Dart](https://github.com/supabase/supabase-flutter/tree/main/packages/supabase) libraries.
|
||||
|
||||
## Supabase Discord
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Resumable uploads divides the file into chunks before uploading them, emitting p
|
||||
/>
|
||||
</video>
|
||||
|
||||
With this release, you can now upload files as large as 50GB! (Previously the limit was 5GB).
|
||||
With this release, users on the Pro tier or higher can now upload files as large as 50GB! This substantial upgrade from the previous limit of 5GB offers even more flexibility for your file uploads.
|
||||
|
||||
To build this feature, we implemented Postgres Advisory locks which solved some gnarly concurrency problems. We can now handle edge-cases, like two clients uploading to the same location. We’ll deep dive into how we implemented Advisory locks later in the post.
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ Markprompt were also impressed with the ease of use and scalability of Supabase.
|
||||
|
||||
## What They Built
|
||||
|
||||
Markprompt is a set of API endpoints that allow you to train your content and create a prompt to ask questions to it, for instance for a docs site. Markprompt also designed a user-friendly web dashboard that simplifies content training, synchronization with GitHub repos or websites, access key management, and provides insights into user query statistics. Furthermore, Markprompt's UI components, available as React and Web Components, allow integration of prompts at all touch points of a company, from public websites websites to internal knowledge bases with just a few lines of code.
|
||||
Markprompt is a set of API endpoints that allow you to train your content and create a prompt to ask questions to it, for instance for a docs site. Markprompt also designed a user-friendly web dashboard that simplifies content training, synchronization with GitHub repos or websites, access key management, and provides insights into user query statistics. Furthermore, Markprompt's UI components, available as React and Web Components, allow integration of prompts at all touch points of a company, from public websites to internal knowledge bases with just a few lines of code.
|
||||
|
||||
Like Supabase, Markprompt is an open-source platform, allowing customers the freedom to host the dashboard and model backend on their own premises. Markprompt warmly welcomes contributions from the community, fostering collaboration and continuous improvement of the platform to meet the evolving needs of developers and technical users.
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const CTABanner = (props: any) => {
|
||||
</h2>
|
||||
</div>
|
||||
<div className="col-span-12 mt-4">
|
||||
<a href="https://app.supabase.com/">
|
||||
<a href="https://supabase.com/dashboard">
|
||||
<Button size="medium" className="text-white">
|
||||
Start your project
|
||||
</Button>
|
||||
|
||||
@@ -5,6 +5,7 @@ import ReactTooltip from 'react-tooltip'
|
||||
import Telemetry from '~/lib/telemetry'
|
||||
import { useTelemetryProps } from 'common/hooks/useTelemetryProps'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useBreakpoint } from 'common'
|
||||
|
||||
const frameworks = [
|
||||
{
|
||||
@@ -54,6 +55,7 @@ const frameworks = [
|
||||
const HeroFrameworks = ({ className }: { className?: string }) => {
|
||||
const router = useRouter()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const isXs = useBreakpoint(640)
|
||||
|
||||
const sendTelemetryEvent = async (gaEvent: string) => {
|
||||
return await Telemetry.sendEvent(
|
||||
@@ -71,7 +73,7 @@ const HeroFrameworks = ({ className }: { className?: string }) => {
|
||||
<div className={['flex text-center flex-col items-center', className].join(' ')}>
|
||||
<small className="small !text-scale-1100">Works seamlessly with 20+ frameworks</small>
|
||||
<div className="w-full sm:max-w-lg mt-4 md:mt-3 lg:ml-0">
|
||||
<div className="flex flex-wrap items-center justify-center gap-2 sm:flex-nowrap">
|
||||
<div className="flex flex-wrap items-center justify-center gap-1 xs:gap-2 sm:flex-nowrap">
|
||||
{frameworks.map((framework) => (
|
||||
<Link href={framework.docs} key={framework.name}>
|
||||
<a
|
||||
@@ -85,8 +87,8 @@ const HeroFrameworks = ({ className }: { className?: string }) => {
|
||||
className="opacity-100 hover:opacity-80 transition-opacity"
|
||||
src={framework.icon}
|
||||
alt={framework.name}
|
||||
width={45}
|
||||
height={45}
|
||||
width={isXs ? 35 : 45}
|
||||
height={isXs ? 35 : 45}
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
@@ -331,14 +331,14 @@ const Nav = () => {
|
||||
</Link>
|
||||
) : (
|
||||
<>
|
||||
<Link href="https://app.supabase.com/">
|
||||
<Link href="https://supabase.com/dashboard">
|
||||
<a>
|
||||
<Button type="default" className="hidden lg:block">
|
||||
Sign in
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
<Link href="https://app.supabase.com/">
|
||||
<Link href="https://supabase.com/dashboard">
|
||||
<a>
|
||||
<Button className="hidden text-white lg:block">Start your project</Button>
|
||||
</a>
|
||||
@@ -395,7 +395,7 @@ const Nav = () => {
|
||||
{/* </div> */}
|
||||
<div className="mt-6 mb-12">
|
||||
<div className="pt-2 pb-4 space-y-1">
|
||||
<Link href="https://app.supabase.com/">
|
||||
<Link href="https://supabase.com/dashboard">
|
||||
<a className="block pl-3 pr-4 text-base font-medium text-scale-900 dark:text-white">
|
||||
Sign in
|
||||
</a>
|
||||
|
||||
@@ -43,7 +43,7 @@ const ProductHeader = (props: Types) => (
|
||||
})}
|
||||
</div>
|
||||
<div className="flex flex-row md:flex-row md:items-center">
|
||||
<Link href="https://app.supabase.com/" as="https://app.supabase.com/">
|
||||
<Link href="https://supabase.com/dashboard" as="https://supabase.com/dashboard">
|
||||
<a>
|
||||
<Button size="medium" className="text-white">
|
||||
Start a project
|
||||
|
||||
@@ -1971,4 +1971,14 @@ module.exports = [
|
||||
source: '/blog/how-supabase-accelerates-development-of-all-pull-together',
|
||||
destination: '/customers',
|
||||
},
|
||||
{
|
||||
permanent: true,
|
||||
source: '/blog/how-supabase-accelerates-development-of-all-pull-together',
|
||||
destination: '/customers',
|
||||
},
|
||||
{
|
||||
permanent: true,
|
||||
source: '/docs/guides/auth/auth-helpers/nextjs-server-components',
|
||||
destination: '/docs/guides/auth/auth-helpers/nextjs',
|
||||
},
|
||||
]
|
||||
@@ -1,3 +1,8 @@
|
||||
import '../../../packages/ui/build/css/themes/light.css'
|
||||
import '../../../packages/ui/build/css/themes/dark.css'
|
||||
|
||||
import '../styles/index.css'
|
||||
|
||||
import { API_URL, APP_NAME, DESCRIPTION } from 'lib/constants'
|
||||
import { DefaultSeo } from 'next-seo'
|
||||
import { AppProps } from 'next/app'
|
||||
|
||||
@@ -98,7 +98,7 @@ function CaseStudyPage(props: any) {
|
||||
"
|
||||
>
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
<div className="col-span-12 mb-2 lg:col-span-2">
|
||||
<div className="col-span-12 mb-2 xl:col-span-2">
|
||||
{/* Back button */}
|
||||
<p>
|
||||
<a
|
||||
@@ -113,7 +113,7 @@ function CaseStudyPage(props: any) {
|
||||
|
||||
<div
|
||||
className="col-span-12 lg:col-span-8
|
||||
|
||||
|
||||
"
|
||||
>
|
||||
<div className="">
|
||||
@@ -146,8 +146,8 @@ function CaseStudyPage(props: any) {
|
||||
objectPosition="left"
|
||||
className="
|
||||
bg-no-repeat
|
||||
|
||||
dark:brightness-200
|
||||
|
||||
dark:brightness-200
|
||||
dark:contrast-0
|
||||
dark:filter
|
||||
"
|
||||
|
||||
@@ -56,7 +56,7 @@ export default function IndexPage() {
|
||||
name: 'Free',
|
||||
nameBadge: '',
|
||||
costUnit: 'per month per project',
|
||||
href: 'https://app.supabase.com/new/new-project',
|
||||
href: 'https://supabase.com/dashboard/new/new-project',
|
||||
priceLabel: 'Starting from',
|
||||
priceMonthly: 0,
|
||||
warning: 'Limit of 2 free projects',
|
||||
@@ -82,7 +82,7 @@ export default function IndexPage() {
|
||||
name: 'Pro',
|
||||
nameBadge: '',
|
||||
costUnit: 'per month per project',
|
||||
href: 'https://app.supabase.com/new/new-project',
|
||||
href: 'https://supabase.com/dashboard/new/new-project',
|
||||
from: true,
|
||||
priceLabel: 'Starting from',
|
||||
warning: '+ usage',
|
||||
@@ -201,7 +201,7 @@ export default function IndexPage() {
|
||||
<p className="p">{priceDescription}</p>
|
||||
</div>
|
||||
<p className="p">{description}</p>
|
||||
<Link href="https://app.supabase.com" passHref>
|
||||
<Link href="https://supabase.com/dashboard" passHref>
|
||||
<a>
|
||||
<Button size="medium" block>
|
||||
Get started
|
||||
@@ -849,7 +849,10 @@ export default function IndexPage() {
|
||||
</th>
|
||||
|
||||
<td className="px-6 pt-5">
|
||||
<Link href="https://app.supabase.com" as="https://app.supabase.com">
|
||||
<Link
|
||||
href="https://supabase.com/dashboard"
|
||||
as="https://supabase.com/dashboard"
|
||||
>
|
||||
<a>
|
||||
<Button size="tiny" type="primary" block>
|
||||
Get started
|
||||
@@ -859,7 +862,10 @@ export default function IndexPage() {
|
||||
</td>
|
||||
|
||||
<td className="px-6 pt-5">
|
||||
<Link href="https://app.supabase.com" as="https://app.supabase.com">
|
||||
<Link
|
||||
href="https://supabase.com/dashboard"
|
||||
as="https://supabase.com/dashboard"
|
||||
>
|
||||
<a>
|
||||
<Button size="tiny" type="primary" block>
|
||||
Get started
|
||||
@@ -916,7 +922,7 @@ export default function IndexPage() {
|
||||
Can't find the answer to your question, you can{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://app.supabase.com/support/new"
|
||||
href="https://supabase.com/dashboard/support/new"
|
||||
className="transition text-brand-900 hover:text-brand-1000"
|
||||
>
|
||||
open a support ticket
|
||||
@@ -927,7 +933,7 @@ export default function IndexPage() {
|
||||
For enterprise enquries,{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://app.supabase.com/support/new"
|
||||
href="https://supabase.com/dashboard/support/new"
|
||||
className="transition text-brand-900 hover:text-brand-1000"
|
||||
>
|
||||
you can contact the team here
|
||||
|
||||
@@ -199,7 +199,7 @@ const Index = ({}: Props) => {
|
||||
border-gray-100 bg-white p-5
|
||||
pt-14 dark:border-gray-600"
|
||||
>
|
||||
<a href="https://app.supabase.com/support/new">
|
||||
<a href="https://supabase.com/dashboard/support/new">
|
||||
<Button size="medium" type="default" iconRight={<IconMail />}>
|
||||
Email Support
|
||||
</Button>
|
||||
|
||||
@@ -1 +1,27 @@
|
||||
module.exports = require('config/tailwind.config')
|
||||
const config = require('config/tailwind.config')
|
||||
|
||||
module.exports = config({
|
||||
content: [
|
||||
'../../packages/common/**/*.{ts,tsx}',
|
||||
'../../packages/ui/**/*.{tsx,ts,js}',
|
||||
'./pages/**/*.{tsx,mdx}',
|
||||
'./components/**/*.tsx',
|
||||
'./layouts/**/*.tsx',
|
||||
'./src/**/*.{ts,tsx,mdx}',
|
||||
'./_blog/*.mdx',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
keyframes: {
|
||||
'flash-code': {
|
||||
'0%': { backgroundColor: 'rgba(63, 207, 142, 0.1)' },
|
||||
'100%': { backgroundColor: 'transparent' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'flash-code': 'flash-code 1s forwards',
|
||||
'flash-code-slow': 'flash-code 2s forwards',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -247,7 +247,7 @@ services:
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
SUPABASE_URL: http://kong:8000"
|
||||
SUPABASE_URL: http://kong:8000
|
||||
SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||
SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY}
|
||||
SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@{POSTGRES_DB}:${POSTGRES_PORT}/${POSTGRES_DB}"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Get these from your API settings: https://app.supabase.com/project/_/settings/api
|
||||
# Get these from your API settings: https://supabase.com/dashboard/project/_/settings/api
|
||||
|
||||
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
|
||||
@@ -1,30 +1,29 @@
|
||||
import { Database } from "@/lib/database.types";
|
||||
import { createServerComponentSupabaseClient } from "@supabase/auth-helpers-nextjs";
|
||||
import { notFound } from "next/navigation";
|
||||
import RealtimePost from "./realtime-post";
|
||||
import { headers, cookies } from "next/headers";
|
||||
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { notFound, redirect } from 'next/navigation'
|
||||
import RealtimePost from './realtime-post'
|
||||
import { cookies } from 'next/headers'
|
||||
|
||||
// do not cache this page
|
||||
export const revalidate = 0;
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
export default async function Post({
|
||||
params: { id },
|
||||
}: {
|
||||
params: { id: string };
|
||||
}) {
|
||||
const supabase = createServerComponentSupabaseClient<Database>({
|
||||
headers,
|
||||
export default async function Post({ params: { id } }: { params: { id: string } }) {
|
||||
const supabase = createServerComponentClient<Database>({
|
||||
cookies,
|
||||
});
|
||||
const { data: post } = await supabase
|
||||
.from("posts")
|
||||
.select()
|
||||
.match({ id })
|
||||
.single();
|
||||
})
|
||||
|
||||
if (!post) {
|
||||
notFound();
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
if (!session) {
|
||||
// this is a protected route - only users who are signed in can view this route
|
||||
redirect('/')
|
||||
}
|
||||
|
||||
return <RealtimePost serverPost={post} />;
|
||||
const { data: post } = await supabase.from('posts').select().match({ id }).single()
|
||||
|
||||
if (!post) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return <RealtimePost serverPost={post} />
|
||||
}
|
||||
@@ -1,40 +1,41 @@
|
||||
"use client";
|
||||
'use client'
|
||||
|
||||
import { Database } from "@/lib/database.types";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useSupabase } from "../supabase-provider";
|
||||
import { useEffect, useState } from 'react'
|
||||
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
type Post = Database["public"]["Tables"]["posts"]["Row"];
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
type Post = Database['public']['Tables']['posts']['Row']
|
||||
|
||||
export default function RealtimePost({ serverPost }: { serverPost: Post }) {
|
||||
const { supabase } = useSupabase();
|
||||
const [post, setPost] = useState(serverPost);
|
||||
const supabase = createClientComponentClient<Database>()
|
||||
const [post, setPost] = useState(serverPost)
|
||||
|
||||
useEffect(() => {
|
||||
setPost(serverPost);
|
||||
}, [serverPost]);
|
||||
setPost(serverPost)
|
||||
}, [serverPost])
|
||||
|
||||
useEffect(() => {
|
||||
const channel = supabase
|
||||
.channel("realtime post")
|
||||
.channel('realtime post')
|
||||
.on(
|
||||
"postgres_changes",
|
||||
'postgres_changes',
|
||||
{
|
||||
event: "UPDATE",
|
||||
schema: "public",
|
||||
table: "posts",
|
||||
event: 'UPDATE',
|
||||
schema: 'public',
|
||||
table: 'posts',
|
||||
filter: `id=eq.${post.id}`,
|
||||
},
|
||||
(payload) => {
|
||||
setPost(payload.new as Post);
|
||||
setPost(payload.new as Post)
|
||||
}
|
||||
)
|
||||
.subscribe();
|
||||
.subscribe()
|
||||
|
||||
return () => {
|
||||
supabase.removeChannel(channel);
|
||||
};
|
||||
}, [supabase, post, setPost]);
|
||||
supabase.removeChannel(channel)
|
||||
}
|
||||
}, [supabase, post, setPost])
|
||||
|
||||
return <pre>{JSON.stringify(post, null, 2)}</pre>;
|
||||
return <pre>{JSON.stringify(post, null, 2)}</pre>
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import { createRouteHandlerSupabaseClient } from "@supabase/auth-helpers-nextjs";
|
||||
import { NextResponse } from "next/server";
|
||||
import { headers, cookies } from "next/headers";
|
||||
|
||||
import type { Database } from "@/lib/database.types";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const { content } = await request.json();
|
||||
|
||||
const supabase = createRouteHandlerSupabaseClient<Database>({
|
||||
headers,
|
||||
cookies,
|
||||
});
|
||||
|
||||
const { data } = await supabase.from("posts").insert({ content }).select();
|
||||
|
||||
return NextResponse.json(data);
|
||||
}
|
||||
|
||||
export async function PUT(request: Request) {
|
||||
const { id, content } = await request.json();
|
||||
|
||||
const supabase = createRouteHandlerSupabaseClient<Database>({
|
||||
headers,
|
||||
cookies,
|
||||
});
|
||||
|
||||
const { data } = await supabase
|
||||
.from("posts")
|
||||
.update({ content })
|
||||
.match({ id })
|
||||
.select();
|
||||
|
||||
return NextResponse.json(data);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { cookies } from 'next/headers'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const requestUrl = new URL(request.url)
|
||||
const code = requestUrl.searchParams.get('code')
|
||||
|
||||
if (code) {
|
||||
const supabase = createRouteHandlerClient<Database>({ cookies })
|
||||
await supabase.auth.exchangeCodeForSession(code)
|
||||
}
|
||||
|
||||
// URL to redirect to after sign in process completes
|
||||
return NextResponse.redirect(requestUrl.origin)
|
||||
}
|
||||
@@ -1,21 +1,19 @@
|
||||
import "./globals.css";
|
||||
import SupabaseProvider from "./supabase-provider";
|
||||
import './globals.css'
|
||||
import Login from './login'
|
||||
|
||||
export const metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
title: 'Create Next App',
|
||||
description: 'Generated by create next app',
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<SupabaseProvider>{children}</SupabaseProvider>
|
||||
{/* @ts-expect-error next version of TS will fix this */}
|
||||
<Login />
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
'use client'
|
||||
|
||||
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useState } from 'react'
|
||||
|
||||
import type { Session } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
export default function LoginForm({ session }: { session: Session | null }) {
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const router = useRouter()
|
||||
const supabase = createClientComponentClient()
|
||||
|
||||
const handleSignUp = async () => {
|
||||
await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: {
|
||||
emailRedirectTo: `${location.origin}/auth/callback`,
|
||||
},
|
||||
})
|
||||
router.refresh()
|
||||
}
|
||||
|
||||
const handleSignIn = async () => {
|
||||
await supabase.auth.signInWithPassword({
|
||||
email,
|
||||
password,
|
||||
})
|
||||
router.refresh()
|
||||
}
|
||||
|
||||
const handleSignOut = async () => {
|
||||
await supabase.auth.signOut()
|
||||
router.refresh()
|
||||
}
|
||||
|
||||
// for the `session` to be available on first SSR render, it must be
|
||||
// fetched in a Server Component and passed down as a prop
|
||||
return session ? (
|
||||
<button onClick={handleSignOut}>Sign out</button>
|
||||
) : (
|
||||
<>
|
||||
<input name="email" onChange={(e) => setEmail(e.target.value)} value={email} />
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
value={password}
|
||||
/>
|
||||
<button onClick={handleSignUp}>Sign up</button>
|
||||
<button onClick={handleSignIn}>Sign in</button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,33 +1,15 @@
|
||||
"use client";
|
||||
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { cookies } from 'next/headers'
|
||||
import LoginForm from './login-form'
|
||||
|
||||
import { useSupabase } from "./supabase-provider";
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
export default function Login() {
|
||||
const { supabase } = useSupabase();
|
||||
export default async function Login() {
|
||||
const supabase = createServerComponentClient<Database>({ cookies })
|
||||
|
||||
const handleSignUp = async () => {
|
||||
await supabase.auth.signUp({
|
||||
email: "jon@supabase.com",
|
||||
password: "sup3rs3cur3",
|
||||
});
|
||||
};
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
const handleLogin = async () => {
|
||||
await supabase.auth.signInWithPassword({
|
||||
email: "jon@supabase.com",
|
||||
password: "sup3rs3cur3",
|
||||
});
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
await supabase.auth.signOut();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={handleSignUp}>Sign Up</button>
|
||||
<button onClick={handleLogin}>Login</button>
|
||||
<button onClick={handleLogout}>Logout</button>
|
||||
</div>
|
||||
);
|
||||
return <LoginForm session={session} />
|
||||
}
|
||||
@@ -1,22 +1,21 @@
|
||||
"use client";
|
||||
import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { revalidatePath } from 'next/cache'
|
||||
import { cookies } from 'next/headers'
|
||||
|
||||
import { FormEvent } from "react";
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
export default function NewPost() {
|
||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
const { content } = Object.fromEntries(new FormData(e.currentTarget));
|
||||
|
||||
await fetch("http://localhost:3000/api/posts", {
|
||||
method: "post",
|
||||
body: JSON.stringify({ content }),
|
||||
});
|
||||
};
|
||||
const addPost = async (formData: FormData) => {
|
||||
'use server'
|
||||
const content = String(formData.get('content'))
|
||||
const supabase = createServerActionClient<Database>({ cookies })
|
||||
await supabase.from('posts').insert({ content }).select()
|
||||
revalidatePath('/')
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<form action={addPost}>
|
||||
<input name="content" />
|
||||
</form>
|
||||
);
|
||||
)
|
||||
}
|
||||
@@ -1,26 +1,20 @@
|
||||
import { createServerComponentSupabaseClient } from "@supabase/auth-helpers-nextjs";
|
||||
import { headers, cookies } from "next/headers";
|
||||
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { cookies } from 'next/headers'
|
||||
import RealtimePosts from './realtime-posts'
|
||||
import NewPost from './new-post'
|
||||
|
||||
import type { Database } from "@/lib/database.types";
|
||||
import RealtimePosts from "./realtime-posts";
|
||||
import Login from "./login";
|
||||
import NewPost from "./new-post";
|
||||
|
||||
// do not cache this page
|
||||
export const revalidate = 0;
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
export default async function ServerComponent() {
|
||||
const supabase = createServerComponentSupabaseClient<Database>({
|
||||
headers,
|
||||
const supabase = createServerComponentClient<Database>({
|
||||
cookies,
|
||||
});
|
||||
const { data } = await supabase.from("posts").select("*");
|
||||
})
|
||||
const { data } = await supabase.from('posts').select('*')
|
||||
|
||||
return (
|
||||
<>
|
||||
<Login />
|
||||
<NewPost />
|
||||
<RealtimePosts serverPosts={data ?? []} />
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
@@ -1,38 +1,40 @@
|
||||
"use client";
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useSupabase } from "./supabase-provider";
|
||||
import { useEffect, useState } from 'react'
|
||||
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
|
||||
|
||||
import type { Database } from "@/lib/database.types";
|
||||
import type { Database } from '@/lib/database.types'
|
||||
import Link from 'next/link'
|
||||
type Post = Database['public']['Tables']['posts']['Row']
|
||||
|
||||
type Post = Database["public"]["Tables"]["posts"]["Row"];
|
||||
|
||||
export default function RealtimePosts({
|
||||
serverPosts,
|
||||
}: {
|
||||
serverPosts: Post[];
|
||||
}) {
|
||||
const [posts, setPosts] = useState(serverPosts);
|
||||
const { supabase } = useSupabase();
|
||||
export default function RealtimePosts({ serverPosts }: { serverPosts: Post[] }) {
|
||||
const [posts, setPosts] = useState(serverPosts)
|
||||
const supabase = createClientComponentClient<Database>()
|
||||
|
||||
useEffect(() => {
|
||||
setPosts(serverPosts);
|
||||
}, [serverPosts]);
|
||||
setPosts(serverPosts)
|
||||
}, [serverPosts])
|
||||
|
||||
useEffect(() => {
|
||||
const channel = supabase
|
||||
.channel("*")
|
||||
.on(
|
||||
"postgres_changes",
|
||||
{ event: "INSERT", schema: "public", table: "posts" },
|
||||
(payload) => setPosts((posts) => [...posts, payload.new as Post])
|
||||
.channel('*')
|
||||
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'posts' }, (payload) =>
|
||||
setPosts((posts) => [...posts, payload.new as Post])
|
||||
)
|
||||
.subscribe();
|
||||
.subscribe()
|
||||
|
||||
return () => {
|
||||
supabase.removeChannel(channel);
|
||||
};
|
||||
}, [supabase, setPosts, posts]);
|
||||
supabase.removeChannel(channel)
|
||||
}
|
||||
}, [supabase, setPosts, posts])
|
||||
|
||||
return <pre>{JSON.stringify(posts, null, 2)}</pre>;
|
||||
return (
|
||||
<>
|
||||
{posts.map((post) => (
|
||||
<div key={post.id}>
|
||||
<Link href={`/${post.id}`}>{post.content}</Link>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { createContext, useContext, useEffect, useState } from "react";
|
||||
import { 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 SupabaseContext = {
|
||||
supabase: SupabaseClient<Database>;
|
||||
};
|
||||
|
||||
const Context = createContext<SupabaseContext | undefined>(undefined);
|
||||
|
||||
export default function SupabaseProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const [supabase] = useState(() => createBrowserSupabaseClient());
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
const {
|
||||
data: { subscription },
|
||||
} = supabase.auth.onAuthStateChange(() => {
|
||||
router.refresh();
|
||||
});
|
||||
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [router, supabase]);
|
||||
|
||||
return (
|
||||
<Context.Provider value={{ supabase }}>
|
||||
<>{children}</>
|
||||
</Context.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useSupabase = () => {
|
||||
let context = useContext(Context);
|
||||
|
||||
if (context === undefined) {
|
||||
throw new Error("useSupabase must be used inside SupabaseProvider");
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
@@ -1,21 +1,12 @@
|
||||
import { createMiddlewareSupabaseClient } from "@supabase/auth-helpers-nextjs";
|
||||
import { NextResponse } from "next/server";
|
||||
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
import type { NextRequest } from "next/server";
|
||||
import type { Database } from "./lib/database.types";
|
||||
import type { NextRequest } from 'next/server'
|
||||
import type { Database } from '@/lib/database.types'
|
||||
|
||||
export async function middleware(req: NextRequest) {
|
||||
const res = NextResponse.next();
|
||||
const supabase = createMiddlewareSupabaseClient<Database>({ req, res });
|
||||
|
||||
const { data: { session } } = await supabase.auth.getSession(); // destructure the data object to obtain the session object
|
||||
|
||||
if (session === null) return NextResponse.redirect(new URL("/login", req.nextUrl));
|
||||
// instead of "/login" you can redirect to any other route in case the user is not logged in
|
||||
|
||||
return res;
|
||||
const res = NextResponse.next()
|
||||
const supabase = createMiddlewareClient<Database>({ req, res })
|
||||
await supabase.auth.getSession()
|
||||
return res
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [], // add the routes you wish the middleware to run in. You can also use regex
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
experimental: {
|
||||
appDir: true,
|
||||
serverActions: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Generated
+753
-454
File diff suppressed because it is too large.
Load diff
@@ -9,15 +9,17 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@supabase/auth-helpers-nextjs": "^0.5.6",
|
||||
"@types/node": "18.15.3",
|
||||
"@types/react": "18.0.28",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"eslint": "8.36.0",
|
||||
"eslint-config-next": "13.2.4",
|
||||
"next": "13.2.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"typescript": "4.9.5"
|
||||
"@supabase/auth-helpers-nextjs": "^0.7.0-next.8",
|
||||
"@types/node": "^20.2.3",
|
||||
"@types/react": "^18.2.6",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"eslint": "^8.41.0",
|
||||
"next": "^13.4.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-config-next": "^13.4.3"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
# these values can be found in your project's API settings
|
||||
# https://app.supabase.com/project/_/settings/api
|
||||
# https://supabase.com/dashboard/project/_/settings/api
|
||||
|
||||
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
|
||||
@@ -17,7 +17,7 @@ export default function RealtimePosts({ serverPosts }: { serverPosts: any }) {
|
||||
|
||||
useEffect(() => {
|
||||
// ensure you have enabled replication on the `posts` table
|
||||
// https://app.supabase.com/project/_/database/replication
|
||||
// https://supabase.com/dashboard/project/_/database/replication
|
||||
const channel = supabase
|
||||
.channel('*')
|
||||
.on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'posts' }, (payload) =>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Get these from your Dashboard: https://app.supabase.com/project/_/settings/api
|
||||
# Get these from your Dashboard: https://supabase.com/dashboard/project/_/settings/api
|
||||
REACT_APP_SUPABASE_URL=
|
||||
REACT_APP_SUPABASE_ANON_KEY=
|
||||
@@ -15,7 +15,7 @@ DB_HOSTNAME=
|
||||
DB_PASSWORD=
|
||||
DB_SSL_CERT="-----BEGIN CERTIFICATE-----
|
||||
GET YOUR CERT FROM YOUR PROJECT DASHBOARD
|
||||
https://app.supabase.com/project/_/settings/database
|
||||
https://supabase.com/dashboard/project/_/settings/database
|
||||
-----END CERTIFICATE-----"
|
||||
|
||||
# puppeteer
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Image Captioning with Supabase Storage and Huggingface.js
|
||||
|
||||
[Hugging Face](https://huggingface.co/) is the collaboration platform for the machine learning community.
|
||||
|
||||
[Huggingface.js](https://huggingface.co/docs/huggingface.js/index) provides a convenient way to make calls to 100,000+ Machine Learning models, making it easy to incorporate AI functionality into your [Supabase Edge Functions](https://supabase.com/edge-functions).
|
||||
|
||||
Putting this together with [Supabase Storage](https://supabase.com/storage) and [Database Webhooks](https://supabase.com/docs/guides/database/webhooks) we can easily put together a service that automatically generates captions for any image we upload to a storage bucket.
|
||||
|
||||
## Setup
|
||||
|
||||
1. Open your Supabase project dashboard or [create a new project](https://app.supabase.com/projects).
|
||||
2. [Create a new bucket](https://app.supabase.com/project/_/storage/buckets) called `images`.
|
||||
3. Generate TypeScript types from remote Database.
|
||||
4. Create a new Database table called `image_caption`.
|
||||
|
||||
- Create `id` column of type `uuid` which references `storage.objects.id`.
|
||||
- Create a `caption` column of type `text`.
|
||||
|
||||
5. Regenerate TypeScript types to include new `image_caption` table.
|
||||
6. Deploy the function to Supabase: `supabase functions deploy huggingface-image-captioning`.
|
||||
7. Create the Database Webhook in the [Supabase Dashboard](https://app.supabase.com/project/_/database/hooks) to trigger the `huggingface-image-captioning` function anytime a record is added to the `storage.objects` table.
|
||||
|
||||
## Generate TypeScript Types
|
||||
|
||||
To generate the types.ts file for the storage and public schemas, run the following command in the terminal:
|
||||
|
||||
```bash
|
||||
supabase gen types typescript --project-id=your-project-ref --schema=storage,public > supabase/functions/huggingface-image-captioning/types.ts
|
||||
```
|
||||
@@ -0,0 +1,49 @@
|
||||
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
|
||||
import { HfInference } from 'https://esm.sh/@huggingface/inference@2.3.2'
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { Database } from './types.ts'
|
||||
|
||||
console.log('Hello from `huggingface-image-captioning` function!')
|
||||
|
||||
const hf = new HfInference(Deno.env.get('HUGGINGFACE_ACCESS_TOKEN'))
|
||||
|
||||
type SoRecord = Database['storage']['Tables']['objects']['Row']
|
||||
interface WebhookPayload {
|
||||
type: 'INSERT' | 'UPDATE' | 'DELETE'
|
||||
table: string
|
||||
record: SoRecord
|
||||
schema: 'public'
|
||||
old_record: null | SoRecord
|
||||
}
|
||||
|
||||
serve(async (req) => {
|
||||
const payload: WebhookPayload = await req.json()
|
||||
const soRecord = payload.record
|
||||
const supabaseAdminClient = createClient<Database>(
|
||||
// Supabase API URL - env var exported by default when deployed.
|
||||
Deno.env.get('SUPABASE_URL') ?? '',
|
||||
// Supabase API SERVICE ROLE KEY - env var exported by default when deployed.
|
||||
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
|
||||
)
|
||||
|
||||
// Construct image url from storage
|
||||
const { data, error } = await supabaseAdminClient.storage
|
||||
.from(soRecord.bucket_id!)
|
||||
.createSignedUrl(soRecord.path_tokens!.join('/'), 60)
|
||||
if (error) throw error
|
||||
const { signedUrl } = data
|
||||
|
||||
// Run image captioning with Huggingface
|
||||
const imgDesc = await hf.imageToText({
|
||||
data: await (await fetch(signedUrl)).blob(),
|
||||
model: 'nlpconnect/vit-gpt2-image-captioning',
|
||||
})
|
||||
|
||||
// Store image caption in Database table
|
||||
await supabaseAdminClient
|
||||
.from('image_caption')
|
||||
.insert({ id: soRecord.id!, caption: imgDesc.generated_text })
|
||||
.throwOnError()
|
||||
|
||||
return new Response('ok')
|
||||
})
|
||||
@@ -0,0 +1,202 @@
|
||||
export type Json =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| { [key: string]: Json }
|
||||
| Json[];
|
||||
|
||||
export interface Database {
|
||||
public: {
|
||||
Tables: {
|
||||
image_caption: {
|
||||
Row: {
|
||||
caption: string;
|
||||
id: string;
|
||||
};
|
||||
Insert: {
|
||||
caption: string;
|
||||
id: string;
|
||||
};
|
||||
Update: {
|
||||
caption?: string;
|
||||
id?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
Views: {
|
||||
[_ in never]: never;
|
||||
};
|
||||
Functions: {
|
||||
[_ in never]: never;
|
||||
};
|
||||
Enums: {
|
||||
[_ in never]: never;
|
||||
};
|
||||
CompositeTypes: {
|
||||
[_ in never]: never;
|
||||
};
|
||||
};
|
||||
storage: {
|
||||
Tables: {
|
||||
buckets: {
|
||||
Row: {
|
||||
allowed_mime_types: string[] | null;
|
||||
avif_autodetection: boolean | null;
|
||||
created_at: string | null;
|
||||
file_size_limit: number | null;
|
||||
id: string;
|
||||
name: string;
|
||||
owner: string | null;
|
||||
public: boolean | null;
|
||||
updated_at: string | null;
|
||||
};
|
||||
Insert: {
|
||||
allowed_mime_types?: string[] | null;
|
||||
avif_autodetection?: boolean | null;
|
||||
created_at?: string | null;
|
||||
file_size_limit?: number | null;
|
||||
id: string;
|
||||
name: string;
|
||||
owner?: string | null;
|
||||
public?: boolean | null;
|
||||
updated_at?: string | null;
|
||||
};
|
||||
Update: {
|
||||
allowed_mime_types?: string[] | null;
|
||||
avif_autodetection?: boolean | null;
|
||||
created_at?: string | null;
|
||||
file_size_limit?: number | null;
|
||||
id?: string;
|
||||
name?: string;
|
||||
owner?: string | null;
|
||||
public?: boolean | null;
|
||||
updated_at?: string | null;
|
||||
};
|
||||
};
|
||||
migrations: {
|
||||
Row: {
|
||||
executed_at: string | null;
|
||||
hash: string;
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
Insert: {
|
||||
executed_at?: string | null;
|
||||
hash: string;
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
Update: {
|
||||
executed_at?: string | null;
|
||||
hash?: string;
|
||||
id?: number;
|
||||
name?: string;
|
||||
};
|
||||
};
|
||||
objects: {
|
||||
Row: {
|
||||
bucket_id: string | null;
|
||||
created_at: string | null;
|
||||
id: string;
|
||||
last_accessed_at: string | null;
|
||||
metadata: Json | null;
|
||||
name: string | null;
|
||||
owner: string | null;
|
||||
path_tokens: string[] | null;
|
||||
updated_at: string | null;
|
||||
version: string | null;
|
||||
};
|
||||
Insert: {
|
||||
bucket_id?: string | null;
|
||||
created_at?: string | null;
|
||||
id?: string;
|
||||
last_accessed_at?: string | null;
|
||||
metadata?: Json | null;
|
||||
name?: string | null;
|
||||
owner?: string | null;
|
||||
path_tokens?: string[] | null;
|
||||
updated_at?: string | null;
|
||||
version?: string | null;
|
||||
};
|
||||
Update: {
|
||||
bucket_id?: string | null;
|
||||
created_at?: string | null;
|
||||
id?: string;
|
||||
last_accessed_at?: string | null;
|
||||
metadata?: Json | null;
|
||||
name?: string | null;
|
||||
owner?: string | null;
|
||||
path_tokens?: string[] | null;
|
||||
updated_at?: string | null;
|
||||
version?: string | null;
|
||||
};
|
||||
};
|
||||
};
|
||||
Views: {
|
||||
[_ in never]: never;
|
||||
};
|
||||
Functions: {
|
||||
can_insert_object: {
|
||||
Args: {
|
||||
bucketid: string;
|
||||
name: string;
|
||||
owner: string;
|
||||
metadata: Json;
|
||||
};
|
||||
Returns: undefined;
|
||||
};
|
||||
extension: {
|
||||
Args: {
|
||||
name: string;
|
||||
};
|
||||
Returns: string;
|
||||
};
|
||||
filename: {
|
||||
Args: {
|
||||
name: string;
|
||||
};
|
||||
Returns: string;
|
||||
};
|
||||
foldername: {
|
||||
Args: {
|
||||
name: string;
|
||||
};
|
||||
Returns: unknown;
|
||||
};
|
||||
get_size_by_bucket: {
|
||||
Args: Record<PropertyKey, never>;
|
||||
Returns: {
|
||||
size: number;
|
||||
bucket_id: string;
|
||||
}[];
|
||||
};
|
||||
search: {
|
||||
Args: {
|
||||
prefix: string;
|
||||
bucketname: string;
|
||||
limits?: number;
|
||||
levels?: number;
|
||||
offsets?: number;
|
||||
search?: string;
|
||||
sortcolumn?: string;
|
||||
sortorder?: string;
|
||||
};
|
||||
Returns: {
|
||||
name: string;
|
||||
id: string;
|
||||
updated_at: string;
|
||||
created_at: string;
|
||||
last_accessed_at: string;
|
||||
metadata: Json;
|
||||
}[];
|
||||
};
|
||||
};
|
||||
Enums: {
|
||||
[_ in never]: never;
|
||||
};
|
||||
CompositeTypes: {
|
||||
[_ in never]: never;
|
||||
};
|
||||
};
|
||||
}
|
||||
Generated
+12641
-58
File diff suppressed because it is too large.
Load diff
@@ -14,7 +14,6 @@
|
||||
"eslint-config-next": "^12.0.8",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-react": "7.28.0",
|
||||
"mini-svg-data-uri": "^1.4.4",
|
||||
"tailwindcss-radix": "^2.0.0",
|
||||
"@tailwindcss/typography": "^0.5.2"
|
||||
}
|
||||
|
||||
@@ -1,30 +1,115 @@
|
||||
const ui = require('./ui.config.js')
|
||||
const svgToDataUri = require('mini-svg-data-uri')
|
||||
const deepMerge = require('deepmerge')
|
||||
|
||||
module.exports = ui({
|
||||
const color = require('./../ui/build/css/tw-extend/color')
|
||||
|
||||
/**
|
||||
* Generates Tailwind colors for the theme
|
||||
* adds <alpha-value> as part of the hsl value
|
||||
*/
|
||||
function generateTwColorClasses(globalKey, twAttributes) {
|
||||
let classes = {}
|
||||
Object.values(twAttributes).map((attr, i) => {
|
||||
const attrKey = Object.keys(twAttributes)[i]
|
||||
if (attrKey.includes(globalKey)) {
|
||||
classes = {
|
||||
...classes,
|
||||
[attrKey.split('-').splice(1).join('-')]: `hsl(${attr.cssVariable} / <alpha-value>)`,
|
||||
}
|
||||
}
|
||||
})
|
||||
/**
|
||||
* mutate object into nested object for tailwind theme structure
|
||||
*/
|
||||
const nestedClasses = kebabToNested(classes)
|
||||
// return, but nest the keys if they are kebab case named
|
||||
return nestedClasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to convert kebab named keys in object to nested nodes
|
||||
*/
|
||||
function kebabToNested(obj) {
|
||||
const result = {}
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const parts = key.split('-')
|
||||
let currentObj = result
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const part = parts[i]
|
||||
if (!currentObj[part]) {
|
||||
currentObj[part] = {}
|
||||
}
|
||||
if (i === parts.length - 1) {
|
||||
if (typeof value === 'object') {
|
||||
currentObj[part] = nestObject(value)
|
||||
} else {
|
||||
currentObj[part] = value
|
||||
}
|
||||
} else {
|
||||
currentObj = currentObj[part]
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Main theme config
|
||||
*/
|
||||
const uiConfig = ui({
|
||||
mode: 'JIT',
|
||||
content: [
|
||||
'../../packages/common/**/*.{ts,tsx}',
|
||||
'../../packages/ui/**/*.{tsx,ts,js}',
|
||||
'./src/**/*.{ts,tsx,mdx}',
|
||||
'./components/**/*.tsx',
|
||||
'./layouts/**/*.tsx',
|
||||
'./pages/**/*.{tsx,mdx}',
|
||||
'./docs/**/*.{tsx,mdx}',
|
||||
'./_blog/*.mdx',
|
||||
// purge styles from supabase ui theme
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
/**
|
||||
* Spread all theme colors and custom generated colors into theme
|
||||
*/
|
||||
textColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
...generateTwColorClasses('textColor', color),
|
||||
}),
|
||||
accentColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
...generateTwColorClasses('accentColor', color),
|
||||
}),
|
||||
backgroundColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
...generateTwColorClasses('backgroundColor', color),
|
||||
}),
|
||||
borderColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
DEFAULT: theme('colors.scale.300', 'currentColor'),
|
||||
dark: theme('colors.scale.1200', 'currentColor'),
|
||||
...generateTwColorClasses('borderColor', color),
|
||||
}),
|
||||
boxShadowColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
...generateTwColorClasses('boxShadowColor', color),
|
||||
}),
|
||||
caretColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
...generateTwColorClasses('caretColor', color),
|
||||
}),
|
||||
divideColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
DEFAULT: theme('colors.scale.300', 'currentColor'),
|
||||
dark: theme('colors.scale.600', 'currentColor'),
|
||||
...generateTwColorClasses('divideColor', color),
|
||||
}),
|
||||
outlineColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
...generateTwColorClasses('outlineColor', color),
|
||||
}),
|
||||
placeholderColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
...generateTwColorClasses('placeholderColor', color),
|
||||
}),
|
||||
ringColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
...generateTwColorClasses('ringColor', color),
|
||||
}),
|
||||
ringOffsetColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
...generateTwColorClasses('ringOffsetColor', color),
|
||||
}),
|
||||
textDecorationColor: (theme) => ({
|
||||
...theme('colors'),
|
||||
...generateTwColorClasses('textDecorationColor', color),
|
||||
}),
|
||||
extend: {
|
||||
typography: ({ theme }) => ({
|
||||
@@ -210,43 +295,73 @@ module.exports = ui({
|
||||
},
|
||||
},
|
||||
},
|
||||
// used in auto docs
|
||||
docs: {
|
||||
css: {
|
||||
'--tw-prose-body': theme('colors.scale[1100]'),
|
||||
'--tw-prose-headings': theme('colors.scale[1200]'),
|
||||
'--tw-prose-lead': theme('colors.scale[1100]'),
|
||||
'--tw-prose-links': theme('colors.brand[900]'),
|
||||
'--tw-prose-bold': theme('colors.scale[1100]'),
|
||||
'--tw-prose-counters': theme('colors.scale[1100]'),
|
||||
'--tw-prose-bullets': theme('colors.scale[900]'),
|
||||
'--tw-prose-hr': theme('colors.scale[500]'),
|
||||
'--tw-prose-quotes': theme('colors.scale[1100]'),
|
||||
'--tw-prose-quote-borders': theme('colors.scale[500]'),
|
||||
'--tw-prose-captions': theme('colors.scale[700]'),
|
||||
'--tw-prose-code': theme('colors.scale[1200]'),
|
||||
'--tw-prose-pre-code': theme('colors.scale[900]'),
|
||||
'--tw-prose-pre-bg': theme('colors.scale[400]'),
|
||||
'--tw-prose-th-borders': theme('colors.scale[500]'),
|
||||
'--tw-prose-td-borders': theme('colors.scale[200]'),
|
||||
'--tw-prose-invert-body': theme('colors.scale[200]'),
|
||||
'--tw-prose-invert-headings': theme('colors.white'),
|
||||
'--tw-prose-invert-lead': theme('colors.scale[500]'),
|
||||
'--tw-prose-invert-links': theme('colors.white'),
|
||||
'--tw-prose-invert-bold': theme('colors.white'),
|
||||
'--tw-prose-invert-counters': theme('colors.scale[400]'),
|
||||
'--tw-prose-invert-bullets': theme('colors.scale[600]'),
|
||||
'--tw-prose-invert-hr': theme('colors.scale[700]'),
|
||||
'--tw-prose-invert-quotes': theme('colors.scale[100]'),
|
||||
'--tw-prose-invert-quote-borders': theme('colors.scale[700]'),
|
||||
'--tw-prose-invert-captions': theme('colors.scale[400]'),
|
||||
// the following are typography overrides
|
||||
// examples can be seen here —> https://github.com/tailwindlabs/tailwindcss-typography/blob/master/src/styles.js
|
||||
// reset all header font weights
|
||||
'h1, h2, h3, h4, h5': {
|
||||
fontWeight: '400',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
screens: {
|
||||
xs: '480px',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['custom-font', 'Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif'],
|
||||
sans: ['Circular', 'custom-font', 'Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif'],
|
||||
mono: ['Office Code Pro', 'Source Code Pro', 'Menlo', 'monospace'],
|
||||
},
|
||||
backgroundImage: (theme) => ({
|
||||
squiggle: `url("${svgToDataUri(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 3" enable-background="new 0 0 6 3" width="6" height="3" fill="${theme(
|
||||
'colors.yellow.400'
|
||||
)}"><polygon points="5.5,0 2.5,3 1.1,3 4.1,0"/><polygon points="4,0 6,2 6,0.6 5.4,0"/><polygon points="0,2 1,3 2.4,3 0,0.6"/></svg>`
|
||||
)}")`,
|
||||
}),
|
||||
keyframes: {
|
||||
'flash-code': {
|
||||
'0%': { backgroundColor: 'rgba(63, 207, 142, 0.1)' },
|
||||
'100%': { backgroundColor: 'transparent' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'flash-code': 'flash-code 1s forwards',
|
||||
'flash-code-slow': 'flash-code 2s forwards',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/typography'),
|
||||
function ({ addUtilities, addVariant }) {
|
||||
addUtilities({
|
||||
// prose (tailwind typography) helpers
|
||||
// useful for removing margins in prose styled sections
|
||||
'.prose--remove-p-margin p': {
|
||||
margin: '0',
|
||||
},
|
||||
})
|
||||
},
|
||||
],
|
||||
plugins: [require('@tailwindcss/typography')],
|
||||
})
|
||||
|
||||
function arrayMergeFn(destinationArray, sourceArray) {
|
||||
return destinationArray.concat(sourceArray).reduce((acc, cur) => {
|
||||
if (acc.includes(cur)) return acc
|
||||
return [...acc, cur]
|
||||
}, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge Supabase UI and Tailwind CSS configurations
|
||||
* @param {object} tailwindConfig - Tailwind config object
|
||||
* @return {object} new config object
|
||||
*/
|
||||
function wrapper(tailwindConfig) {
|
||||
return deepMerge({ ...tailwindConfig }, uiConfig, {
|
||||
arrayMerge: arrayMergeFn,
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = wrapper
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"sourceType": "unambiguous",
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"chrome": 100
|
||||
}
|
||||
}
|
||||
],
|
||||
"@babel/preset-typescript",
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
// stories: [
|
||||
// '../src/components/**/*.stories.mdx',
|
||||
// '../src/components/**/*.stories.@(js|jsx|ts|tsx)',
|
||||
// ],
|
||||
// TODO: Temporally Import one by one story and fix build issue.
|
||||
// After all stories are cleaned, revert to wildcard import.
|
||||
// // stories: [
|
||||
// // '../src/components/**/*.stories.mdx',
|
||||
// // '../src/components/**/*.stories.@(js|jsx|ts|tsx)',
|
||||
// // ],
|
||||
// // TODO: Temporally Import one by one story and fix build issue.
|
||||
// // After all stories are cleaned, revert to wildcard import.
|
||||
stories: [
|
||||
'../src/components/Accordion/Accordion.stories.tsx',
|
||||
'../src/components/Alert/Alert.stories.tsx',
|
||||
@@ -18,43 +16,27 @@ module.exports = {
|
||||
'../src/components/Dropdown/Dropdown.stories.tsx',
|
||||
],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-interactions',
|
||||
// '@storybook/addon-links',
|
||||
// '@storybook/addon-essentials',
|
||||
// '@storybook/addon-interactions',
|
||||
// '@storybook/addon-actions',
|
||||
'storybook-dark-mode',
|
||||
{
|
||||
name: '@storybook/addon-postcss',
|
||||
name: '@storybook/addon-styling',
|
||||
options: {
|
||||
postcssLoaderOptions: {
|
||||
implementation: require('postcss'),
|
||||
},
|
||||
// Check out https://github.com/storybookjs/addon-styling/blob/main/docs/api.md
|
||||
// For more details on this addon's options.
|
||||
postCss: true,
|
||||
},
|
||||
},
|
||||
'@storybook/addon-mdx-gfm',
|
||||
],
|
||||
framework: '@storybook/react',
|
||||
webpackFinal: async (config, { configType }) => {
|
||||
const cssRuleIdx = config.module.rules.findIndex(({ test }) => test.toString() === '/\\.css$/')
|
||||
const cssRule = {
|
||||
test: /\.css$/,
|
||||
include: path.resolve(__dirname, '../'),
|
||||
exclude: /\.module\.css$/,
|
||||
use: [
|
||||
{ loader: 'style-loader' },
|
||||
{ loader: 'css-loader', options: { importLoaders: 1, sourceMap: true } },
|
||||
{ loader: 'postcss-loader', options: {} },
|
||||
],
|
||||
}
|
||||
const cssModulesRule = {
|
||||
test: /\.module.css$/,
|
||||
include: path.resolve(__dirname, '../'),
|
||||
use: [
|
||||
{ loader: 'style-loader' },
|
||||
{ loader: 'css-loader', options: { importLoaders: 1, modules: true, sourceMap: true } },
|
||||
{ loader: 'postcss-loader', options: {} },
|
||||
],
|
||||
}
|
||||
config.module.rules.splice(cssRuleIdx, 1, cssModulesRule, cssRule)
|
||||
|
||||
return config
|
||||
docs: {
|
||||
autodocs: true,
|
||||
},
|
||||
framework: {
|
||||
name: '@storybook/react-webpack5',
|
||||
options: {},
|
||||
},
|
||||
}
|
||||
@@ -2,9 +2,13 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@import './../build/css/themes/dark.css';
|
||||
@import './../build/css/themes/light.css';
|
||||
@import './../build/css/themes/darker-dark.css';
|
||||
|
||||
.dark,
|
||||
.light,
|
||||
.light .docs-story,
|
||||
.dark .docs-story {
|
||||
@apply bg-scale-200;
|
||||
@apply bg-body;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
# Supabase UI Package
|
||||
|
||||
## Figma-tokens setup
|
||||
|
||||
Tokens setup is based partly on blogpost on the [Mirahi Digital Garden](https://garden.mirahi.io/how-to-use-the-color-tokens-from-your-design-system-directly-in-tailwind-css/).
|
||||
|
||||
Transforms tokens stored on [Figma Tokens](https://github.com/six7/figma-tokens) using [token-transformer](https://github.com/six7/figma-tokens/tree/main/token-transformer) and [Style Dictionary](https://github.com/amzn/style-dictionary) to css-variables with references, and use them in your [TailwindCSS](https://github.com/tailwindlabs/tailwindcss) environment with multiple themes.
|
||||
|
||||
### Build all the styles files (css-variables and tailwind config)
|
||||
|
||||
```bash
|
||||
npm run build-styles
|
||||
```
|
||||
|
||||
This creates a directory called `styles` with the tokens and also CSS required.
|
||||
|
||||
### Other examples
|
||||
|
||||
You can find [other examples here](https://github.com/six7/figma-tokens-examples) by [Jan Six](https://twitter.com/six7)
|
||||
|
||||
### Adding new themes/sets
|
||||
|
||||
Add new sets by extending the scripts in package.json. Currently theme sets derive from `exported/[set name]` but can come from anywhere.
|
||||
|
||||
Adding a new set can be done with the following:
|
||||
|
||||
```json package.json
|
||||
// package.json
|
||||
|
||||
//..
|
||||
"build-transform-light": "npx token-transformer tokens.json styles/tokens/02_themes/light.json global,exported/light,theme global",
|
||||
// add a new line like this:
|
||||
"build-transform-new": "npx token-transformer tokens.json styles/tokens/02_themes/new.json global,new,theme global",
|
||||
//..
|
||||
// append the new theme/set command to end of the build-transform command
|
||||
"build-transform": "npm run build-transform-global && npm run build-transform-typography && npm run build-transform-dark && npm run build-transform-light && npm run build-transform-new",
|
||||
|
||||
```
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Do not edit directly
|
||||
* Generated on Tue, 09 May 2023 15:35:29 GMT
|
||||
*/
|
||||
|
||||
.dark {
|
||||
--utility-mix-negative: 0deg 0% 0%;
|
||||
--utility-mix-positive: 0deg 0% 100%;
|
||||
--darken-percentage: 0.2;
|
||||
--shading-hardest-xll: 0.85;
|
||||
--shading-hardest-xl: 0.75;
|
||||
--shading-hardest: 0.65;
|
||||
--shading-harder: 0.5;
|
||||
--shading-hard: 0.3;
|
||||
--shading-soft: 0.05;
|
||||
--shading-default: 0.1;
|
||||
--text-override-lighten-strength: 0;
|
||||
--theme-variables-text-body: 0deg 0% 92.9%; /* og color is colors.Gray.Dark.12 */
|
||||
--theme-variables-success: 123.8deg 100% 66.3%;
|
||||
--theme-variables-danger: 0deg 100% 64.7%;
|
||||
--theme-variables-warning: 26.9deg 93.8% 56.1%;
|
||||
--theme-variables-background-body-alt: 0deg 0% 8.6%; /* og color is colors.Gray.Dark.1 */
|
||||
--theme-variables-background-body: 0deg 0% 11%; /* og color is colors.Gray.Dark.2 */
|
||||
--theme-variables-background: 0deg 0% 13.7%; /* og color is colors.Gray.Dark.3 */
|
||||
--theme-variables-brand: 153.1deg 60.2% 52.7%;
|
||||
--text-color-disabled-default: 0deg 0% 46.5%;
|
||||
--text-color-danger-default: var(--theme-variables-danger);
|
||||
--text-color-warning-default: var(--theme-variables-warning);
|
||||
--text-color-link-default: var(--theme-variables-brand);
|
||||
--text-color-success-default: var(--theme-variables-success);
|
||||
--text-color-body-default: var(--theme-variables-text-body);
|
||||
--background-color-danger-default: var(--theme-variables-danger);
|
||||
--background-color-danger-300: 6.9deg 29% 16.2%;
|
||||
--background-color-danger-200: 3.6deg 46.8% 35%;
|
||||
--background-color-danger-100: 2.1deg 50.9% 46.5%;
|
||||
--background-color-warning-default: var(--theme-variables-warning);
|
||||
--background-color-warning-300: 24.5deg 32.6% 15.4%;
|
||||
--background-color-warning-200: 25.7deg 57.5% 31.7%;
|
||||
--background-color-warning-100: 26.2deg 64.9% 41.4%;
|
||||
--background-color-success-default: var(--theme-variables-success);
|
||||
--background-color-success-300: 114deg 21.2% 16.6%;
|
||||
--background-color-success-200: 119deg 37.3% 36.8%;
|
||||
--background-color-success-100: 121deg 43% 48.6%;
|
||||
--background-color-body-alt: var(--theme-variables-background-body-alt);
|
||||
--background-color-body: var(--theme-variables-background-body); /* Background color used for the main page body. */
|
||||
--background-color-default: var(--theme-variables-background); /* Default background color for any container. */
|
||||
--text-color-body-lightest: 91.8deg 0% 30.2%;
|
||||
--text-color-body-lighter: 91.8deg 0% 43.5%;
|
||||
--text-color-body-light: 91.8deg 0% 62.5%;
|
||||
--border-color-selection: 91.8deg 0% 53.5%;
|
||||
--border-color-body-alt: 91.8deg 0% 12.2%;
|
||||
--border-color-body: 91.8deg 0% 14.6%;
|
||||
--border-color-default: 91.8deg 0% 17.3%; /* Default border color */
|
||||
--background-color-control: 91.8deg 0% 10.4%;
|
||||
--background-color-selection: 91.8deg 0% 17.3%;
|
||||
--border-color-control: 91.8deg 0% 20.8%;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Do not edit directly
|
||||
* Generated on Tue, 09 May 2023 15:35:29 GMT
|
||||
*/
|
||||
|
||||
.light {
|
||||
--utility-github-background: 0deg 0% 100%;
|
||||
--utility-mix-negative: 0deg 0% 100%;
|
||||
--utility-mix-positive: 0deg 0% 0%;
|
||||
--darken-percentage: 0.2;
|
||||
--shading-hardest-xll: 0.85;
|
||||
--shading-hardest-xl: 0.75;
|
||||
--shading-hardest: 0.65;
|
||||
--shading-harder: 0.5;
|
||||
--shading-hard: 0.3;
|
||||
--shading-soft: 0.035;
|
||||
--shading-default: 0.1;
|
||||
--text-override-lighten-strength: 5;
|
||||
--theme-variables-text-body: 195deg 7.1% 11%; /* og color is colors.Slate.Dark.2 */
|
||||
--theme-variables-success: 123.8deg 100% 66.3%;
|
||||
--theme-variables-danger: 0deg 100% 64.7%;
|
||||
--theme-variables-warning: 26.9deg 93.8% 56.1%;
|
||||
--theme-variables-background-body-alt: 0deg 0% 100%; /* og color is colors.Gray.Dark.1 */
|
||||
--theme-variables-background-body: 210deg 16.7% 97.6%; /* og color is colors.Slate.Light.2 */
|
||||
--theme-variables-background: 210deg 33.3% 98.8%; /* og color is colors.Slate.Light.1 */
|
||||
--theme-variables-brand: 153.1deg 60.2% 52.7%;
|
||||
--text-color-disabled-default: 195deg 7.1% 5.5%;
|
||||
--text-color-danger-default: var(--theme-variables-danger);
|
||||
--text-color-warning-default: var(--theme-variables-warning);
|
||||
--text-color-link-default: var(--theme-variables-brand);
|
||||
--text-color-success-default: var(--theme-variables-success);
|
||||
--text-color-body-default: var(--theme-variables-text-body);
|
||||
--background-color-danger-default: var(--theme-variables-danger);
|
||||
--background-color-danger-300: 8deg 100% 95.2%;
|
||||
--background-color-danger-200: 6.9deg 100% 83.3%;
|
||||
--background-color-danger-100: 5.3deg 100% 76.2%;
|
||||
--background-color-warning-default: var(--theme-variables-warning);
|
||||
--background-color-warning-300: 23.5deg 100% 94.4%;
|
||||
--background-color-warning-200: 24.6deg 100% 80.1%;
|
||||
--background-color-warning-100: 25.2deg 100% 71.2%;
|
||||
--background-color-success-default: var(--theme-variables-success);
|
||||
--background-color-success-300: 112deg 100% 95.7%;
|
||||
--background-color-success-200: 114deg 100% 85.3%;
|
||||
--background-color-success-100: 117deg 100% 78.9%;
|
||||
--background-color-body-alt: var(--theme-variables-background-body-alt);
|
||||
--background-color-body: var(--theme-variables-background-body); /* Background color used for the main page body. */
|
||||
--background-color-default: var(--theme-variables-background); /* Default background color for any container. */
|
||||
--text-color-body-lightest: 195deg 1% 65.6%;
|
||||
--text-color-body-lighter: 195deg 1% 51.8%;
|
||||
--text-color-body-light: 195deg 1.9% 34.3%;
|
||||
--border-color-selection: 210deg 0.4% 46.1%;
|
||||
--border-color-body-alt: 91.8deg 0% 96%;
|
||||
--border-color-body: 210deg 6.1% 93.7%;
|
||||
--border-color-default: 210deg 7.5% 94.9%; /* Default border color */
|
||||
--background-color-control: 210deg 33.3% 99.2%;
|
||||
--background-color-selection: 210deg 7.5% 94.9%;
|
||||
--border-color-control: 210deg 4.1% 91.2%;
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
module.exports = {
|
||||
"theme-variables-brand": {
|
||||
cssVariable: "var(--theme-variables-brand)",
|
||||
value: "hsl(153.10000000000002, 60.2%, 52.7%)"
|
||||
},
|
||||
"theme-variables-background": {
|
||||
cssVariable: "var(--theme-variables-background)",
|
||||
value: "hsl(0, 0%, 13.7%)"
|
||||
},
|
||||
"theme-variables-background-body": {
|
||||
cssVariable: "var(--theme-variables-background-body)",
|
||||
value: "hsl(0, 0%, 11%)"
|
||||
},
|
||||
"theme-variables-background-body-alt": {
|
||||
cssVariable: "var(--theme-variables-background-body-alt)",
|
||||
value: "hsl(0, 0%, 8.6%)"
|
||||
},
|
||||
"theme-variables-warning": {
|
||||
cssVariable: "var(--theme-variables-warning)",
|
||||
value: "hsl(26.899999999999977, 93.8%, 56.1%)"
|
||||
},
|
||||
"theme-variables-danger": {
|
||||
cssVariable: "var(--theme-variables-danger)",
|
||||
value: "hsl(0, 100%, 64.7%)"
|
||||
},
|
||||
"theme-variables-success": {
|
||||
cssVariable: "var(--theme-variables-success)",
|
||||
value: "hsl(123.80000000000001, 100%, 66.3%)"
|
||||
},
|
||||
"theme-variables-text-body": {
|
||||
cssVariable: "var(--theme-variables-text-body)",
|
||||
value: "hsl(0, 0%, 92.9%)"
|
||||
},
|
||||
"backgroundColor-DEFAULT": {
|
||||
cssVariable: "var(--background-color-default)",
|
||||
value: "hsl(0, 0%, 13.7%)"
|
||||
},
|
||||
"backgroundColor-body": {
|
||||
cssVariable: "var(--background-color-body)",
|
||||
value: "hsl(0, 0%, 11%)"
|
||||
},
|
||||
"backgroundColor-body-alt": {
|
||||
cssVariable: "var(--background-color-body-alt)",
|
||||
value: "hsl(0, 0%, 8.6%)"
|
||||
},
|
||||
"backgroundColor-selection": {
|
||||
cssVariable: "var(--background-color-selection)",
|
||||
value: "hsl(91.8 0% 17.3%)"
|
||||
},
|
||||
"backgroundColor-control": {
|
||||
cssVariable: "var(--background-color-control)",
|
||||
value: "hsl(91.8 0% 10.4%)"
|
||||
},
|
||||
"backgroundColor-success-100": {
|
||||
cssVariable: "var(--background-color-success-100)",
|
||||
value: "hsl(121 43% 48.6%)"
|
||||
},
|
||||
"backgroundColor-success-200": {
|
||||
cssVariable: "var(--background-color-success-200)",
|
||||
value: "hsl(119 37.3% 36.8%)"
|
||||
},
|
||||
"backgroundColor-success-300": {
|
||||
cssVariable: "var(--background-color-success-300)",
|
||||
value: "hsl(114 21.2% 16.6%)"
|
||||
},
|
||||
"backgroundColor-success-DEFAULT": {
|
||||
cssVariable: "var(--background-color-success-default)",
|
||||
value: "hsl(123.80000000000001, 100%, 66.3%)"
|
||||
},
|
||||
"backgroundColor-warning-100": {
|
||||
cssVariable: "var(--background-color-warning-100)",
|
||||
value: "hsl(26.2 64.9% 41.4%)"
|
||||
},
|
||||
"backgroundColor-warning-200": {
|
||||
cssVariable: "var(--background-color-warning-200)",
|
||||
value: "hsl(25.7 57.5% 31.7%)"
|
||||
},
|
||||
"backgroundColor-warning-300": {
|
||||
cssVariable: "var(--background-color-warning-300)",
|
||||
value: "hsl(24.5 32.6% 15.4%)"
|
||||
},
|
||||
"backgroundColor-warning-DEFAULT": {
|
||||
cssVariable: "var(--background-color-warning-default)",
|
||||
value: "hsl(26.899999999999977, 93.8%, 56.1%)"
|
||||
},
|
||||
"backgroundColor-danger-100": {
|
||||
cssVariable: "var(--background-color-danger-100)",
|
||||
value: "hsl(2.07 50.9% 46.5%)"
|
||||
},
|
||||
"backgroundColor-danger-200": {
|
||||
cssVariable: "var(--background-color-danger-200)",
|
||||
value: "hsl(3.65 46.8% 35%)"
|
||||
},
|
||||
"backgroundColor-danger-300": {
|
||||
cssVariable: "var(--background-color-danger-300)",
|
||||
value: "hsl(6.9 29% 16.2%)"
|
||||
},
|
||||
"backgroundColor-danger-DEFAULT": {
|
||||
cssVariable: "var(--background-color-danger-default)",
|
||||
value: "hsl(0, 100%, 64.7%)"
|
||||
},
|
||||
"borderColor-DEFAULT": {
|
||||
cssVariable: "var(--border-color-default)",
|
||||
value: "hsl(91.8 0% 17.3%)"
|
||||
},
|
||||
"borderColor-body": {
|
||||
cssVariable: "var(--border-color-body)",
|
||||
value: "hsl(91.8 0% 14.6%)"
|
||||
},
|
||||
"borderColor-body-alt": {
|
||||
cssVariable: "var(--border-color-body-alt)",
|
||||
value: "hsl(91.8 0% 12.2%)"
|
||||
},
|
||||
"borderColor-selection": {
|
||||
cssVariable: "var(--border-color-selection)",
|
||||
value: "hsl(91.8 0% 53.5%)"
|
||||
},
|
||||
"borderColor-control": {
|
||||
cssVariable: "var(--border-color-control)",
|
||||
value: "hsl(91.8 0% 20.8%)"
|
||||
},
|
||||
"textColor-body-DEFAULT": {
|
||||
cssVariable: "var(--text-color-body-default)",
|
||||
value: "hsl(0, 0%, 92.9%)"
|
||||
},
|
||||
"textColor-body-light": {
|
||||
cssVariable: "var(--text-color-body-light)",
|
||||
value: "hsl(91.8 0% 62.5%)"
|
||||
},
|
||||
"textColor-body-lighter": {
|
||||
cssVariable: "var(--text-color-body-lighter)",
|
||||
value: "hsl(91.8 0% 43.5%)"
|
||||
},
|
||||
"textColor-body-lightest": {
|
||||
cssVariable: "var(--text-color-body-lightest)",
|
||||
value: "hsl(91.8 0% 30.2%)"
|
||||
},
|
||||
"textColor-success-DEFAULT": {
|
||||
cssVariable: "var(--text-color-success-default)",
|
||||
value: "hsl(123.80000000000001, 100%, 66.3%)"
|
||||
},
|
||||
"textColor-link-DEFAULT": {
|
||||
cssVariable: "var(--text-color-link-default)",
|
||||
value: "hsl(153.10000000000002, 60.2%, 52.7%)"
|
||||
},
|
||||
"textColor-warning-DEFAULT": {
|
||||
cssVariable: "var(--text-color-warning-default)",
|
||||
value: "hsl(26.899999999999977, 93.8%, 56.1%)"
|
||||
},
|
||||
"textColor-danger-DEFAULT": {
|
||||
cssVariable: "var(--text-color-danger-default)",
|
||||
value: "hsl(0, 100%, 64.7%)"
|
||||
},
|
||||
"textColor-disabled-DEFAULT": {
|
||||
cssVariable: "var(--text-color-disabled-default)",
|
||||
value: "hsl(0 0% 46.5%)"
|
||||
},
|
||||
"utility-mix-positive": {
|
||||
cssVariable: "var(--utility-mix-positive)",
|
||||
value: "hsl(0, 0%, 100%)"
|
||||
},
|
||||
"utility-mix-negative": {
|
||||
cssVariable: "var(--utility-mix-negative)",
|
||||
value: "hsl(0, 0%, 0%)"
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
const deepMerge = require('deepmerge')
|
||||
const customFormsPlugin = require('@tailwindcss/forms')
|
||||
|
||||
const backgroundOpacity = (theme) => ({
|
||||
10: '0.1',
|
||||
...theme('opacity'),
|
||||
})
|
||||
|
||||
const maxHeight = (theme) => ({
|
||||
0: '0',
|
||||
xl: '36rem',
|
||||
...theme('spacing'),
|
||||
})
|
||||
|
||||
const windmillConfig = {
|
||||
darkMode: 'class',
|
||||
purge: {
|
||||
content: [
|
||||
//'node_modules/@supabase/ui/theme/defaultTheme.js',
|
||||
// 'node_modules/@supabase/ui/dist/index.js',
|
||||
],
|
||||
},
|
||||
theme: {
|
||||
backgroundOpacity,
|
||||
maxHeight,
|
||||
},
|
||||
variants: {
|
||||
backgroundOpacity: ['responsive', 'hover', 'focus', 'dark'],
|
||||
backgroundColor: ['responsive', 'hover', 'focus', 'active', 'odd', 'dark'],
|
||||
display: ['responsive', 'dark'],
|
||||
textColor: ['responsive', 'focus', 'focus-within', 'hover', 'active', 'dark'],
|
||||
placeholderColor: ['responsive', 'focus', 'dark'],
|
||||
borderColor: ['responsive', 'hover', 'focus', 'dark'],
|
||||
divideColor: ['responsive', 'dark'],
|
||||
boxShadow: ['responsive', 'hover', 'focus', 'dark'],
|
||||
margin: ['responsive', 'last'],
|
||||
},
|
||||
plugins: [customFormsPlugin],
|
||||
}
|
||||
|
||||
function arrayMergeFn(destinationArray, sourceArray) {
|
||||
return destinationArray.concat(sourceArray).reduce((acc, cur) => {
|
||||
if (acc.includes(cur)) return acc
|
||||
return [...acc, cur]
|
||||
}, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge Windmill and Tailwind CSS configurations
|
||||
* @param {object} tailwindConfig - Tailwind config object
|
||||
* @return {object} new config object
|
||||
*/
|
||||
export function wrapper(tailwindConfig) {
|
||||
let purge
|
||||
if (Array.isArray(tailwindConfig.purge)) {
|
||||
purge = {
|
||||
content: tailwindConfig.purge,
|
||||
}
|
||||
} else {
|
||||
purge = tailwindConfig.purge
|
||||
}
|
||||
return deepMerge({ ...tailwindConfig, purge }, windmillConfig, {
|
||||
arrayMerge: arrayMergeFn,
|
||||
})
|
||||
}
|
||||
|
||||
// module.exports = wrapper
|
||||
@@ -0,0 +1,248 @@
|
||||
const glob = require('glob')
|
||||
|
||||
const configFiles = glob.sync(`tokens/config/**/*.json`)
|
||||
const sourceFiles = glob.sync(`tokens/source/**/*.json`)
|
||||
const themeFiles = glob.sync(`tokens/themes/**/*.json`)
|
||||
const semanticFiles = glob.sync(`tokens/semantic/**/*.json`)
|
||||
|
||||
const { registerTransforms } = require('@tokens-studio/sd-transforms')
|
||||
const StyleDictionary = require('style-dictionary')
|
||||
|
||||
const Color = require('color')
|
||||
|
||||
/**
|
||||
* uncomment tailwind types as they become available
|
||||
*/
|
||||
const supportedTokenTypeList = [
|
||||
// 'spacing',
|
||||
// 'sizing',
|
||||
// 'borderRadius',
|
||||
// 'borderWidth',
|
||||
'color',
|
||||
// 'opacity',
|
||||
// 'fontFamilies',
|
||||
// 'lineHeights',
|
||||
// 'letterSpacing',
|
||||
// 'paragraphSpacing',
|
||||
// 'fontWeights',
|
||||
// 'fontSizes',
|
||||
// 'textCase',
|
||||
// 'textDecoration',
|
||||
]
|
||||
|
||||
registerTransforms(StyleDictionary)
|
||||
|
||||
const formatTailwindValue = (tokenType, value) => {
|
||||
let formattedValue
|
||||
switch (tokenType) {
|
||||
case 'color':
|
||||
default:
|
||||
formattedValue = value
|
||||
}
|
||||
return formattedValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom format that generates tailwind friendly colors so we can add <alpha-value>
|
||||
* in the config later
|
||||
* https://tailwindcss.com/docs/customizing-colors#using-css-variables
|
||||
*/
|
||||
StyleDictionary.registerTransform({
|
||||
name: 'color/rgb',
|
||||
type: 'value',
|
||||
matcher: (prop) => prop.type === 'color',
|
||||
transformer: (prop) => {
|
||||
const color = Color(prop.original.value).hsl()
|
||||
return color.string()
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Custom format that generate tailwind color config based on css variables
|
||||
*/
|
||||
StyleDictionary.registerFormat({
|
||||
name: 'tw/css-variables',
|
||||
formatter({ dictionary }) {
|
||||
// console.log('dictionary', dictionary)
|
||||
return (
|
||||
'module.exports = ' +
|
||||
`{\n${dictionary.allProperties
|
||||
.map((token) => {
|
||||
const value = formatTailwindValue(token.type, token.value)
|
||||
return `"${token.path.slice(0).join('-')}": {
|
||||
cssVariable: "var(--${token.name})",
|
||||
value: "${value}"
|
||||
}`
|
||||
})
|
||||
.join(',\n')}\n}`
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Returns the files configuration
|
||||
* for generating separated tailwind files.
|
||||
*/
|
||||
function getConfigTailwindFilesByType(typeList) {
|
||||
console.log('\n')
|
||||
console.log('tailwind typeList', typeList)
|
||||
return typeList.map((typeName) => {
|
||||
return {
|
||||
destination: `tw-extend/${typeName}.js`,
|
||||
format: 'tw/css-variables',
|
||||
filter: {
|
||||
type: typeName,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getStyleDictionaryConfig(
|
||||
sourceFiles,
|
||||
fileName,
|
||||
type,
|
||||
buildTailwindFiles = false,
|
||||
rootTheme = false
|
||||
) {
|
||||
console.log(sourceFiles)
|
||||
|
||||
let configTailwindFilesByType = []
|
||||
|
||||
if (buildTailwindFiles) {
|
||||
console.log(`\n`)
|
||||
console.log(`Generating Tailwind Props ✨`)
|
||||
configTailwindFilesByType = getConfigTailwindFilesByType(supportedTokenTypeList)
|
||||
}
|
||||
|
||||
return {
|
||||
source: sourceFiles,
|
||||
platforms: {
|
||||
css: {
|
||||
// transformGroup: 'tokens-studio',
|
||||
transforms: [
|
||||
'ts/descriptionToComment',
|
||||
'ts/size/px',
|
||||
'ts/opacity',
|
||||
'ts/size/lineheight',
|
||||
'ts/type/fontWeight',
|
||||
'ts/resolveMath',
|
||||
'ts/size/css/letterspacing',
|
||||
'ts/typography/css/shorthand',
|
||||
'ts/border/css/shorthand',
|
||||
'ts/shadow/css/shorthand',
|
||||
'ts/color/css/hexrgba',
|
||||
'ts/color/modifiers',
|
||||
'name/cti/kebab',
|
||||
'color/rgb',
|
||||
],
|
||||
buildPath: 'build/css/',
|
||||
files: [
|
||||
{
|
||||
destination: `${type}/${fileName}.css`,
|
||||
format: 'css/variables',
|
||||
options: {
|
||||
selector: rootTheme ? ':root' : `.${fileName}`,
|
||||
outputReferences: true,
|
||||
},
|
||||
// filter: (token) =>
|
||||
// [sourceFiles.slice(-1)[0], ...semanticFiles, ...sourceFiles].includes(token.filePath),
|
||||
},
|
||||
...configTailwindFilesByType,
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function fileNameCleaner(fileName) {
|
||||
return fileName.split('/').pop().replace('.json', '')
|
||||
}
|
||||
|
||||
/**
|
||||
* BUILD SOURCE FILES
|
||||
*/
|
||||
|
||||
sourceFiles.map(function (filePath) {
|
||||
const fileName = fileNameCleaner(filePath)
|
||||
const SD = StyleDictionary.extend(
|
||||
getStyleDictionaryConfig([filePath], fileName, 'source', true, true)
|
||||
)
|
||||
SD.buildAllPlatforms()
|
||||
})
|
||||
|
||||
/**
|
||||
* BUILD THEME FILE
|
||||
*/
|
||||
|
||||
themeFiles.map(function (filePath, i) {
|
||||
const buildTailwindFiles = filePath.includes('root') // i === 0
|
||||
const fileName = fileNameCleaner(filePath)
|
||||
const rootTheme = filePath.includes('root')
|
||||
const SD = StyleDictionary.extend(
|
||||
getStyleDictionaryConfig(
|
||||
// determine whether to include the root theme properties with each theme
|
||||
rootTheme ? [filePath] : ['tokens/themes/root.json', filePath],
|
||||
rootTheme ? 'dark' : fileName,
|
||||
'themes',
|
||||
buildTailwindFiles,
|
||||
false
|
||||
)
|
||||
)
|
||||
SD.buildAllPlatforms()
|
||||
})
|
||||
|
||||
function convertToVariableIfNeeded(value) {
|
||||
if (value.startsWith('{') && value.endsWith('}')) {
|
||||
return `var(--${value.slice(1, -1).split('.').join('-')})`
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* BUILD TYPOGRAPHY FILES
|
||||
*/
|
||||
|
||||
/**
|
||||
* Format for css typography classes
|
||||
* This generates theme-independent css classes so we're fine with just using css variables here
|
||||
*/
|
||||
StyleDictionary.registerFormat({
|
||||
name: 'css/typographyClasses',
|
||||
formatter: (dictionary, config) =>
|
||||
dictionary.allProperties
|
||||
.map((prop) => {
|
||||
return `
|
||||
.${prop.name} {
|
||||
font: var(--${prop.name});
|
||||
letter-spacing: ${convertToVariableIfNeeded(prop.original.value.letterSpacing)};
|
||||
text-transform: ${convertToVariableIfNeeded(prop.original.value.textCase)};
|
||||
text-decoration: ${convertToVariableIfNeeded(prop.original.value.textDecoration)};
|
||||
}`
|
||||
})
|
||||
.join('\n'),
|
||||
})
|
||||
|
||||
function getTypographyConfig() {
|
||||
console.log('Building: typography')
|
||||
return {
|
||||
source: [...semanticFiles, ...sourceFiles],
|
||||
platforms: {
|
||||
css: {
|
||||
transforms: ['name/cti/kebab'],
|
||||
transformGroup: 'tokens-studio',
|
||||
buildPath: 'build/css/',
|
||||
files: [
|
||||
{
|
||||
destination: `source/typography-classes.css`,
|
||||
format: 'css/typographyClasses',
|
||||
selector: ':root',
|
||||
filter: (token) => token.type === 'typography',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const typographyBuild = StyleDictionary.extend(getTypographyConfig())
|
||||
typographyBuild.buildAllPlatforms()
|
||||
@@ -0,0 +1,35 @@
|
||||
const glob = require('glob')
|
||||
const fs = require('fs')
|
||||
|
||||
const cssFiles = glob.sync('build/css/**/*.css')
|
||||
|
||||
function transformHSLValues(precision) {
|
||||
cssFiles.forEach((file) => {
|
||||
const css = fs.readFileSync(file, 'utf8')
|
||||
|
||||
const transformedCss = css.replace(
|
||||
/hsl\(([\d.]+)[,\s]+([\d.]+)%[,\s]+([\d.]+)%\)/g,
|
||||
(match, hue, saturation, lightness) => {
|
||||
hue = `${roundNumber(hue, precision)}deg`
|
||||
saturation = `${roundNumber(saturation, precision)}%`
|
||||
lightness = `${roundNumber(lightness, precision)}%`
|
||||
return `${hue} ${saturation} ${lightness}`
|
||||
}
|
||||
)
|
||||
|
||||
fs.writeFileSync(file, transformedCss)
|
||||
})
|
||||
|
||||
function roundNumber(number, precision) {
|
||||
const rounded = parseFloat(number).toFixed(precision)
|
||||
const str = rounded.toString()
|
||||
const match = str.match(/^(\d+)\.(\d+)$/)
|
||||
if (match && match[2] === '0'.repeat(match[2].length)) {
|
||||
return match[1]
|
||||
}
|
||||
return rounded
|
||||
}
|
||||
}
|
||||
|
||||
// Example usage:
|
||||
transformHSLValues(1)
|
||||
@@ -0,0 +1,78 @@
|
||||
const https = require('https')
|
||||
const fs = require('fs')
|
||||
|
||||
/*
|
||||
* Branch to target in GitHub repo
|
||||
*/
|
||||
const branch = 'feat/new-components'
|
||||
// using Math.random() in a param as GitHub caches raw content
|
||||
// const url = `https://raw.githubusercontent.com/MildTomato/supabase-design-tokens/${branch}/tokens.json?e=${Math.random()}`
|
||||
const baseUrl = `https://raw.githubusercontent.com/MildTomato/supabase-design-tokens/${branch}/tokens/`
|
||||
|
||||
/*
|
||||
* Files that need to be copied over from Tokens repo
|
||||
*/
|
||||
const TOKEN_FILES_METADATA = [
|
||||
// figma tokens misc config files (comment out for now)
|
||||
// { fileName: '$metadata.json', type: 'config' },
|
||||
// { fileName: '$themes.json', type: 'config' },
|
||||
// source files (comment out for now)
|
||||
// { fileName: 'global.json', type: 'source' },
|
||||
// { fileName: 'global-two.json', type: 'source' },
|
||||
// semantic (comment out for now)
|
||||
// { fileName: 'typography.json', type: 'semantic' },
|
||||
// themes
|
||||
{ fileName: 'root.json', type: 'theme', subdirectory: 'new/' }, // root theme
|
||||
{ fileName: 'light.json', type: 'theme', subdirectory: 'new/' },
|
||||
// { fileName: 'darker-dark.json', type: 'theme', subdirectory: 'new/' },
|
||||
]
|
||||
|
||||
const PATHS = {
|
||||
config: 'config/',
|
||||
source: 'source/',
|
||||
theme: 'themes/',
|
||||
semantic: 'semantic/',
|
||||
}
|
||||
|
||||
async function getTokensFile() {
|
||||
const promises = TOKEN_FILES_METADATA.map((tokenMetadata, i) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
https
|
||||
.get(
|
||||
baseUrl + tokenMetadata.subdirectory + tokenMetadata.fileName + `?e=${Math.random()}`,
|
||||
(res) => {
|
||||
let data = ''
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk
|
||||
})
|
||||
res.on('end', () => {
|
||||
fs.writeFile(
|
||||
`./tokens/${PATHS[tokenMetadata.type]}${tokenMetadata.fileName}`,
|
||||
data,
|
||||
'utf8',
|
||||
() => {}
|
||||
)
|
||||
resolve(data)
|
||||
})
|
||||
}
|
||||
)
|
||||
.on('error', (err) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
await Promise.all(promises)
|
||||
}
|
||||
|
||||
async function copyFiles() {
|
||||
console.log('Copying token files... \n')
|
||||
console.log('branch to pull is:', branch)
|
||||
try {
|
||||
await getTokensFile()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
copyFiles()
|
||||
@@ -6,9 +6,13 @@
|
||||
"license": "MIT",
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build-storybook": "build-storybook",
|
||||
"build:icons": "node --experimental-modules internals/populate-icons.js",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-tokens": "node internals/tokens/build-tokens.js",
|
||||
"copy-tokens": "node internals/tokens/copy-tokens.js",
|
||||
"cleanse-css-for-tailwind": "node internals/tokens/cleanse-css-for-tailwind.js",
|
||||
"generate-styles": "npm run copy-tokens && npm run build-tokens && npm run cleanse-css-for-tailwind",
|
||||
"build-storybook": "storybook build",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -25,16 +29,17 @@
|
||||
"@supabase/auth-helpers-react": "^0.3.1",
|
||||
"@tailwindcss/forms": "^0.4.0",
|
||||
"@tailwindcss/typography": "^0.5.0",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"clsx": "^1.2.1",
|
||||
"cmdk-supabase": "^0.2.2",
|
||||
"color": "^4.2.3",
|
||||
"colorjs.io": "^0.4.3",
|
||||
"common-tags": "^1.8.2",
|
||||
"deepmerge": "^4.2.2",
|
||||
"formik": "^2.2.9",
|
||||
"lodash": "^4.17.20",
|
||||
"lucide-react": "^0.124.0",
|
||||
"openai": "^3.2.1",
|
||||
"postcss": "^8.4.5",
|
||||
"postcss": "^8.4.22",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-countdown": "^2.3.5",
|
||||
@@ -48,15 +53,28 @@
|
||||
"tailwindcss-radix": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/react": "^6.5.16",
|
||||
"config": "*",
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/preset-typescript": "^7.21.4",
|
||||
"@storybook/addon-actions": "^7.0.5",
|
||||
"@storybook/addon-mdx-gfm": "^7.0.5",
|
||||
"@storybook/addon-styling": "^1.0.1",
|
||||
"@storybook/react": "^7.0.5",
|
||||
"@storybook/react-webpack5": "^7.0.5",
|
||||
"@tokens-studio/sd-transforms": "^0.8.5",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@types/common-tags": "^1.8.1",
|
||||
"@types/react": "^17.0.37",
|
||||
"@types/react-copy-to-clipboard": "^5.0.4",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/react-syntax-highlighter": "^15.5.6",
|
||||
"copyfiles": "^2.4.1",
|
||||
"glob": "^8.1.0",
|
||||
"sql-formatter": "^12.2.0",
|
||||
"storybook-dark-mode": "^2.1.1",
|
||||
"storybook": "^7.0.5",
|
||||
"storybook-dark-mode": "^3.0.0",
|
||||
"style-dictionary": "^3.7.1",
|
||||
"typescript": "4.3.4"
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ export const Collapsible = ({
|
||||
}: CollapsibleProps) => {
|
||||
return (
|
||||
<RadixCollapsible.Root
|
||||
asChild={props.asChild}
|
||||
defaultOpen={props.defaultOpen}
|
||||
open={open}
|
||||
onOpenChange={props.onOpenChange}
|
||||
@@ -30,15 +31,17 @@ export function Trigger({ children, asChild }: { children: React.ReactNode; asCh
|
||||
}
|
||||
|
||||
export function Content({
|
||||
asChild,
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
asChild?: boolean
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}) {
|
||||
const __styles = styleHandler('collapsible')
|
||||
return (
|
||||
<RadixCollapsible.Content className={[__styles.content, className].join(' ')}>
|
||||
<RadixCollapsible.Content asChild={asChild} className={[__styles.content, className].join(' ')}>
|
||||
{children}
|
||||
</RadixCollapsible.Content>
|
||||
)
|
||||
|
||||
@@ -207,7 +207,7 @@ const CommandMenu = ({ projectRef }: CommandMenuProps) => {
|
||||
{sharedItems.tools.map((item) => {
|
||||
const itemUrl = (
|
||||
projectRef ? item.url.replace('_', projectRef) : item.url
|
||||
).split('https://app.supabase.com')[1]
|
||||
).split('https://supabase.com/dashboard')[1]
|
||||
|
||||
return (
|
||||
<CommandItem
|
||||
|
||||
@@ -2,171 +2,171 @@
|
||||
"tools": [
|
||||
{
|
||||
"label": "Table Editor",
|
||||
"url": "https://app.supabase.com/project/_/editor"
|
||||
"url": "https://supabase.com/dashboard/project/_/editor"
|
||||
},
|
||||
{
|
||||
"label": "SQL Editor",
|
||||
"url": "https://app.supabase.com/project/_/sql"
|
||||
"url": "https://supabase.com/dashboard/project/_/sql"
|
||||
},
|
||||
{
|
||||
"label": "Database",
|
||||
"url": "https://app.supabase.com/project/_/database/tables",
|
||||
"url": "https://supabase.com/dashboard/project/_/database/tables",
|
||||
"subItems": [
|
||||
{
|
||||
"label": "Tables",
|
||||
"url": "https://app.supabase.com/project/_/database/tables"
|
||||
"url": "https://supabase.com/dashboard/project/_/database/tables"
|
||||
},
|
||||
{
|
||||
"label": "Triggers",
|
||||
"url": "https://app.supabase.com/project/_/database/triggers"
|
||||
"url": "https://supabase.com/dashboard/project/_/database/triggers"
|
||||
},
|
||||
{
|
||||
"label": "Functions",
|
||||
"url": "https://app.supabase.com/project/_/database/functions"
|
||||
"url": "https://supabase.com/dashboard/project/_/database/functions"
|
||||
},
|
||||
{
|
||||
"label": "Extensions",
|
||||
"url": "https://app.supabase.com/project/_/database/extensions"
|
||||
"url": "https://supabase.com/dashboard/project/_/database/extensions"
|
||||
},
|
||||
{
|
||||
"label": "Roles",
|
||||
"url": "https://app.supabase.com/project/_/database/roles"
|
||||
"url": "https://supabase.com/dashboard/project/_/database/roles"
|
||||
},
|
||||
{
|
||||
"label": "Replication",
|
||||
"url": "https://app.supabase.com/project/_/database/replication"
|
||||
"url": "https://supabase.com/dashboard/project/_/database/replication"
|
||||
},
|
||||
{
|
||||
"label": "Webhooks",
|
||||
"url": "https://app.supabase.com/project/_/database/webhooks"
|
||||
"url": "https://supabase.com/dashboard/project/_/database/webhooks"
|
||||
},
|
||||
{
|
||||
"label": "Backups",
|
||||
"url": "https://app.supabase.com/project/_/database/backups/scheduled"
|
||||
"url": "https://supabase.com/dashboard/project/_/database/backups/scheduled"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Auth",
|
||||
"url": "https://app.supabase.com/project/_/auth/users",
|
||||
"url": "https://supabase.com/dashboard/project/_/auth/users",
|
||||
"subItems": [
|
||||
{
|
||||
"label": "Users",
|
||||
"url": "https://app.supabase.com/project/_/auth/users"
|
||||
"url": "https://supabase.com/dashboard/project/_/auth/users"
|
||||
},
|
||||
{
|
||||
"label": "Policies",
|
||||
"url": "https://app.supabase.com/project/_/auth/policies"
|
||||
"url": "https://supabase.com/dashboard/project/_/auth/policies"
|
||||
},
|
||||
{
|
||||
"label": "Providers",
|
||||
"url": "https://app.supabase.com/project/_/auth/providers"
|
||||
"url": "https://supabase.com/dashboard/project/_/auth/providers"
|
||||
},
|
||||
{
|
||||
"label": "Email Templates",
|
||||
"url": "https://app.supabase.com/project/_/auth/templates"
|
||||
"url": "https://supabase.com/dashboard/project/_/auth/templates"
|
||||
},
|
||||
{
|
||||
"label": "URL Configuration",
|
||||
"url": "https://app.supabase.com/project/_/auth/url-configuration"
|
||||
"url": "https://supabase.com/dashboard/project/_/auth/url-configuration"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Storage",
|
||||
"url": "https://app.supabase.com/project/_/storage"
|
||||
"url": "https://supabase.com/dashboard/project/_/storage"
|
||||
},
|
||||
{
|
||||
"label": "Edge Functions",
|
||||
"url": "https://app.supabase.com/project/_/functions"
|
||||
"url": "https://supabase.com/dashboard/project/_/functions"
|
||||
},
|
||||
{
|
||||
"label": "Logs",
|
||||
"url": "https://app.supabase.com/project/_/logs/explorer",
|
||||
"url": "https://supabase.com/dashboard/project/_/logs/explorer",
|
||||
"subItems": [
|
||||
{
|
||||
"label": "Postgres logs",
|
||||
"url": "https://app.supabase.com/project/_/logs/postgres-logs"
|
||||
"url": "https://supabase.com/dashboard/project/_/logs/postgres-logs"
|
||||
},
|
||||
{
|
||||
"label": "PostgREST logs",
|
||||
"url": "https://app.supabase.com/project/_/logs/postgrest-logs"
|
||||
"url": "https://supabase.com/dashboard/project/_/logs/postgrest-logs"
|
||||
},
|
||||
{
|
||||
"label": "Postgres logs",
|
||||
"url": "https://app.supabase.com/project/_/logs/pgbouncer-logs"
|
||||
"url": "https://supabase.com/dashboard/project/_/logs/pgbouncer-logs"
|
||||
},
|
||||
{
|
||||
"label": "Auth logs logs",
|
||||
"url": "https://app.supabase.com/project/_/logs/auth-logs"
|
||||
"url": "https://supabase.com/dashboard/project/_/logs/auth-logs"
|
||||
},
|
||||
{
|
||||
"label": "Storage logs",
|
||||
"url": "https://app.supabase.com/project/_/logs/storage-logs"
|
||||
"url": "https://supabase.com/dashboard/project/_/logs/storage-logs"
|
||||
},
|
||||
{
|
||||
"label": "realtime logs",
|
||||
"url": "https://app.supabase.com/project/_/logs/realtime-logs"
|
||||
"url": "https://supabase.com/dashboard/project/_/logs/realtime-logs"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "API Docs",
|
||||
"url": "https://app.supabase.com/project/_/api",
|
||||
"url": "https://supabase.com/dashboard/project/_/api",
|
||||
"subItems": [
|
||||
{
|
||||
"label": "Auth docs",
|
||||
"url": "https://app.supabase.com/project/_/api?page=auth"
|
||||
"url": "https://supabase.com/dashboard/project/_/api?page=auth"
|
||||
},
|
||||
{
|
||||
"label": "User management docs",
|
||||
"url": "https://app.supabase.com/project/_/api?page=users"
|
||||
"url": "https://supabase.com/dashboard/project/_/api?page=users"
|
||||
},
|
||||
{
|
||||
"label": "Graphql",
|
||||
"url": "https://app.supabase.com/project/_/api/graphiql"
|
||||
"url": "https://supabase.com/dashboard/project/_/api/graphiql"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Project Settings",
|
||||
"url": "https://app.supabase.com/project/_/settings/general",
|
||||
"url": "https://supabase.com/dashboard/project/_/settings/general",
|
||||
"subItems": [
|
||||
{
|
||||
"label": "General settings",
|
||||
"url": "https://app.supabase.com/project/_/settings/general"
|
||||
"url": "https://supabase.com/dashboard/project/_/settings/general"
|
||||
},
|
||||
{
|
||||
"label": "Database settings",
|
||||
"url": "https://app.supabase.com/project/_/settings/database"
|
||||
"url": "https://supabase.com/dashboard/project/_/settings/database"
|
||||
},
|
||||
{
|
||||
"label": "API settings",
|
||||
"url": "https://app.supabase.com/project/_/settings/api"
|
||||
"url": "https://supabase.com/dashboard/project/_/settings/api"
|
||||
},
|
||||
{
|
||||
"label": "Auth settings",
|
||||
"url": "https://app.supabase.com/project/_/settings/auth"
|
||||
"url": "https://supabase.com/dashboard/project/_/settings/auth"
|
||||
},
|
||||
{
|
||||
"label": "Storage settings",
|
||||
"url": "https://app.supabase.com/project/_/settings/storage"
|
||||
"url": "https://supabase.com/dashboard/project/_/settings/storage"
|
||||
},
|
||||
{
|
||||
"label": "Subscription settings",
|
||||
"url": "https://app.supabase.com/project/_/settings/billing/subscription"
|
||||
"url": "https://supabase.com/dashboard/project/_/settings/billing/subscription"
|
||||
},
|
||||
{
|
||||
"label": "Project Usage",
|
||||
"url": "https://app.supabase.com/project/_/settings/billing/usage"
|
||||
"url": "https://supabase.com/dashboard/project/_/settings/billing/usage"
|
||||
},
|
||||
{
|
||||
"label": "Project Invoices",
|
||||
"url": "https://app.supabase.com/project/_/settings/billing/invoices"
|
||||
"url": "https://supabase.com/dashboard/project/_/settings/billing/invoices"
|
||||
},
|
||||
{
|
||||
"label": "Project Invoices",
|
||||
"url": "https://app.supabase.com/project/_/settings/billing/invoices"
|
||||
"url": "https://supabase.com/dashboard/project/_/settings/billing/invoices"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -174,11 +174,11 @@
|
||||
"projectTools": [
|
||||
{
|
||||
"label": "Your Projects",
|
||||
"url": "https://app.supabase.com/projects"
|
||||
"url": "https://supabase.com/dashboard/projects"
|
||||
},
|
||||
{
|
||||
"label": "Project API Keys",
|
||||
"url": "https://app.supabase.com/project/_/settings/api"
|
||||
"url": "https://supabase.com/dashboard/project/_/settings/api"
|
||||
}
|
||||
],
|
||||
"support": [
|
||||
|
||||
@@ -25,8 +25,13 @@ const DemoContainer = ({ children }: { children: React.ReactNode }) => (
|
||||
>
|
||||
<div
|
||||
className="
|
||||
bg border
|
||||
|
||||
p-3 px-5
|
||||
bg-scale-300 border border-scale-600 rounded flex gap-4
|
||||
shadow-sm
|
||||
rounded-md
|
||||
flex
|
||||
gap-4
|
||||
justify-between
|
||||
items-center
|
||||
w-3/4
|
||||
@@ -53,9 +58,9 @@ export const DefaultFull = (args: any) => {
|
||||
overlay={[
|
||||
<Dropdown.Misc>
|
||||
<div>
|
||||
<span className="block">Signed in as </span>
|
||||
<span className="block text-body">Signed in as </span>
|
||||
|
||||
<span>tom@example.com </span>
|
||||
<span className="text-body-light">tom@example.com </span>
|
||||
</div>
|
||||
</Dropdown.Misc>,
|
||||
<Dropdown.Separator />,
|
||||
|
||||
@@ -88,6 +88,8 @@ export default function Form({ validate, ...props }: Props) {
|
||||
handleReset: formik.handleReset,
|
||||
/** Resets the form with custom values */
|
||||
resetForm: formik.resetForm,
|
||||
/** Manually sets a fields value */
|
||||
setFieldValue: formik.setFieldValue,
|
||||
})}
|
||||
</FormContextProvider>
|
||||
</form>
|
||||
|
||||
@@ -12,7 +12,8 @@ import './Introduction.css'
|
||||
|
||||
<Meta title="Introduction" />
|
||||
|
||||
<style>{`
|
||||
<style>
|
||||
{`
|
||||
.subheading {
|
||||
--mediumdark: '#999999';
|
||||
font-weight: 900;
|
||||
@@ -114,7 +115,8 @@ import './Introduction.css'
|
||||
}
|
||||
|
||||
|
||||
`}</style>
|
||||
`}
|
||||
</style>
|
||||
|
||||
# Welcome to Storybook
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.sbui-popover__content {
|
||||
@apply bg-bg-primary-light dark:bg-bg-secondary-dark p-0;
|
||||
@apply bg-scale-200 p-0;
|
||||
@apply border;
|
||||
@apply rounded;
|
||||
@apply shadow;
|
||||
@@ -46,7 +46,7 @@
|
||||
} */
|
||||
|
||||
.sbui-popover__arrow {
|
||||
@apply fill-current text-bg-primary-light dark:text-bg-secondary-dark;
|
||||
@apply fill-current text-scale-200;
|
||||
@apply border-0 border-t;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
@@ -135,15 +135,16 @@ function RadioGroup({
|
||||
}
|
||||
|
||||
interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
||||
label: string
|
||||
afterLabel?: string
|
||||
label: string | React.ReactNode
|
||||
afterLabel?: string | React.ReactNode
|
||||
beforeLabel?: string | React.ReactNode
|
||||
description?: string
|
||||
description?: string | React.ReactNode
|
||||
size?: 'tiny' | 'small' | 'medium' | 'large' | 'xlarge'
|
||||
hidden?: boolean
|
||||
align?: 'vertical' | 'horizontal'
|
||||
optionalLabel?: 'string' | React.ReactNode
|
||||
addOnBefore?: React.ReactNode
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
function Radio({
|
||||
@@ -156,6 +157,7 @@ function Radio({
|
||||
description,
|
||||
name,
|
||||
checked,
|
||||
className,
|
||||
onChange,
|
||||
onBlur,
|
||||
hidden = false,
|
||||
@@ -163,6 +165,7 @@ function Radio({
|
||||
align = 'vertical',
|
||||
optionalLabel,
|
||||
addOnBefore,
|
||||
children,
|
||||
}: InputProps) {
|
||||
const __styles = styleHandler('radio')
|
||||
|
||||
@@ -196,6 +199,7 @@ function Radio({
|
||||
activeId === markupId ? true : checked ? true : checked === false ? false : undefined
|
||||
|
||||
let classes = [
|
||||
className,
|
||||
__styles.variants[type].container.base,
|
||||
// __styles.variants[type].container.align[align],
|
||||
type === 'list' && !hidden && __styles.variants[type].container.size[size],
|
||||
@@ -245,38 +249,50 @@ function Radio({
|
||||
onBlur={handleBlurEvent}
|
||||
/>
|
||||
{addOnBefore}
|
||||
<div
|
||||
className={[
|
||||
__styles.label.base,
|
||||
__styles.label[size],
|
||||
__styles.variants[type].container.align[align],
|
||||
].join(' ')}
|
||||
>
|
||||
{beforeLabel && (
|
||||
<span
|
||||
className={[__styles.label_before.base, __styles.label_before[size]].join(' ')}
|
||||
{children || (
|
||||
<>
|
||||
<div
|
||||
className={[
|
||||
__styles.label.base,
|
||||
__styles.label[size],
|
||||
__styles.variants[type].container.align[align],
|
||||
].join(' ')}
|
||||
>
|
||||
{beforeLabel}
|
||||
</span>
|
||||
)}
|
||||
<span>{label}</span>
|
||||
{afterLabel && (
|
||||
<span className={[__styles.label_after.base, __styles.label_after[size]].join(' ')}>
|
||||
{afterLabel}
|
||||
</span>
|
||||
)}
|
||||
{description && (
|
||||
<p className={[__styles.description.base, __styles.description[size]].join(' ')}>
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{optionalLabel && (
|
||||
<div
|
||||
className={[__styles.optionalLabel.base, __styles.optionalLabel[size]].join(' ')}
|
||||
>
|
||||
{optionalLabel}
|
||||
</div>
|
||||
{beforeLabel && (
|
||||
<div
|
||||
className={[__styles.label_before.base, __styles.label_before[size]].join(
|
||||
' '
|
||||
)}
|
||||
>
|
||||
{beforeLabel}
|
||||
</div>
|
||||
)}
|
||||
<div>{label}</div>
|
||||
{afterLabel && (
|
||||
<div
|
||||
className={[__styles.label_after.base, __styles.label_after[size]].join(' ')}
|
||||
>
|
||||
{afterLabel}
|
||||
</div>
|
||||
)}
|
||||
{description && (
|
||||
<div
|
||||
className={[__styles.description.base, __styles.description[size]].join(' ')}
|
||||
>
|
||||
{description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{optionalLabel && (
|
||||
<div
|
||||
className={[__styles.optionalLabel.base, __styles.optionalLabel[size]].join(
|
||||
' '
|
||||
)}
|
||||
>
|
||||
{optionalLabel}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</label>
|
||||
)
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react'
|
||||
import { Button } from '../../../index'
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
import styleHandler from '../../lib/theme/styleHandler'
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
|
||||
export type SidePanelProps = RadixProps & CustomProps
|
||||
|
||||
@@ -18,6 +19,7 @@ interface RadixProps
|
||||
|
||||
interface CustomProps {
|
||||
id?: String | undefined
|
||||
disabled?: boolean
|
||||
className?: String
|
||||
children?: React.ReactNode
|
||||
header?: string | React.ReactNode
|
||||
@@ -32,10 +34,12 @@ interface CustomProps {
|
||||
onConfirm?: () => void
|
||||
confirmText?: String
|
||||
triggerElement?: React.ReactNode
|
||||
tooltip?: string
|
||||
}
|
||||
|
||||
const SidePanel = ({
|
||||
id,
|
||||
disabled,
|
||||
className,
|
||||
children,
|
||||
header,
|
||||
@@ -52,6 +56,7 @@ const SidePanel = ({
|
||||
cancelText = 'Cancel',
|
||||
triggerElement,
|
||||
defaultOpen,
|
||||
tooltip,
|
||||
...props
|
||||
}: SidePanelProps) => {
|
||||
const __styles = styleHandler('sidepanel')
|
||||
@@ -64,17 +69,40 @@ const SidePanel = ({
|
||||
customFooter
|
||||
) : (
|
||||
<div className={__styles.footer}>
|
||||
<Button disabled={loading} type="default" onClick={() => (onCancel ? onCancel() : null)}>
|
||||
{cancelText}
|
||||
</Button>
|
||||
<Button
|
||||
htmlType="submit"
|
||||
disabled={loading}
|
||||
loading={loading}
|
||||
onClick={() => (onConfirm ? onConfirm() : null)}
|
||||
>
|
||||
{confirmText}
|
||||
</Button>
|
||||
<div>
|
||||
<Button disabled={loading} type="default" onClick={() => (onCancel ? onCancel() : null)}>
|
||||
{cancelText}
|
||||
</Button>
|
||||
</div>
|
||||
<Tooltip.Root delayDuration={0}>
|
||||
<Tooltip.Trigger asChild>
|
||||
<div>
|
||||
<Button
|
||||
htmlType="submit"
|
||||
disabled={disabled || loading}
|
||||
loading={loading}
|
||||
onClick={() => (onConfirm ? onConfirm() : null)}
|
||||
>
|
||||
{confirmText}
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip.Trigger>
|
||||
{tooltip !== undefined && (
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content side="bottom">
|
||||
<Tooltip.Arrow className="radix-tooltip-arrow" />
|
||||
<div
|
||||
className={[
|
||||
'rounded bg-scale-100 py-1 px-2 leading-none shadow',
|
||||
'border border-scale-200',
|
||||
].join(' ')}
|
||||
>
|
||||
<span className="text-xs text-scale-1200">{tooltip}</span>
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
)}
|
||||
</Tooltip.Root>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
@@ -879,7 +879,7 @@ export default {
|
||||
horizontal: 'flex flex-row space-x-2',
|
||||
},
|
||||
},
|
||||
group: `flex flex-row gap-3`,
|
||||
group: `grid grid-cols-12 gap-3`,
|
||||
base: `
|
||||
transition
|
||||
border border-scale-700 hover:border-scale-900
|
||||
@@ -1181,7 +1181,6 @@ export default {
|
||||
// root:
|
||||
trigger: `
|
||||
flex
|
||||
|
||||
border-none
|
||||
rounded
|
||||
bg-transparent p-0
|
||||
@@ -1201,8 +1200,8 @@ export default {
|
||||
`,
|
||||
content: `
|
||||
z-40
|
||||
bg-scale-100 dark:bg-scale-300
|
||||
border border-scale-300 dark:border-scale-500
|
||||
bg
|
||||
border
|
||||
rounded
|
||||
shadow-lg
|
||||
py-1.5
|
||||
@@ -1226,26 +1225,25 @@ export default {
|
||||
item: `
|
||||
group
|
||||
relative
|
||||
text-xs
|
||||
text-scale-1100
|
||||
text-sm
|
||||
text-body-light
|
||||
px-4 py-1.5 flex items-center space-x-2
|
||||
cursor-pointer
|
||||
focus:bg-scale-300 dark:focus:bg-scale-500
|
||||
focus:text-scale-1200
|
||||
focus:bg-selection
|
||||
focus:text-body
|
||||
border-none
|
||||
focus:outline-none
|
||||
`,
|
||||
disabled: `opacity-50 cursor-default`,
|
||||
label: `
|
||||
text-scale-900
|
||||
text-body-lighter
|
||||
px-4 flex items-center space-x-2 py-1.5
|
||||
text-xs
|
||||
`,
|
||||
separator: `
|
||||
w-full
|
||||
h-px
|
||||
my-2
|
||||
bg-scale-300 dark:bg-scale-500
|
||||
border-t-[1px]
|
||||
`,
|
||||
misc: `
|
||||
px-4 py-1.5
|
||||
|
||||
@@ -1 +1,29 @@
|
||||
module.exports = require('config/tailwind.config')
|
||||
const config = require('config/tailwind.config')
|
||||
|
||||
module.exports = config({
|
||||
content: [
|
||||
'../../packages/common/**/*.{ts,tsx}',
|
||||
'../../packages/ui/**/*.{tsx,ts,js}',
|
||||
'./pages/**/*.{tsx,mdx}',
|
||||
'./components/**/*.tsx',
|
||||
'./layouts/**/*.tsx',
|
||||
'./src/**/*.{ts,tsx,mdx}',
|
||||
'./_blog/*.mdx',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
keyframes: {
|
||||
'flash-code': {
|
||||
'0%': { backgroundColor: 'rgba(63, 207, 142, 0.1)' },
|
||||
'100%': { backgroundColor: 'transparent' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'flash-code': 'flash-code 1s forwards',
|
||||
'flash-code-slow': 'flash-code 2s forwards',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// module.exports = require('config/tailwind.config')
|
||||
+359
-9
@@ -101,6 +101,126 @@
|
||||
"security": [{ "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/v1/oauth/authorize": {
|
||||
"get": {
|
||||
"operationId": "authorize",
|
||||
"parameters": [
|
||||
{ "name": "client_id", "required": true, "in": "query", "schema": { "type": "string" } },
|
||||
{
|
||||
"name": "response_type",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": { "enum": ["code", "token", "id_token token"], "type": "string" }
|
||||
},
|
||||
{
|
||||
"name": "redirect_uri",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
{ "name": "scope", "required": true, "in": "query", "schema": { "type": "string" } },
|
||||
{ "name": "state", "required": false, "in": "query", "schema": { "type": "string" } },
|
||||
{
|
||||
"name": "response_mode",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
{
|
||||
"name": "code_challenge",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
{
|
||||
"name": "code_challenge_method",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": { "enum": ["plain", "sha256"], "type": "string" }
|
||||
}
|
||||
],
|
||||
"responses": { "200": { "description": "" } },
|
||||
"tags": ["oauth (beta)"]
|
||||
}
|
||||
},
|
||||
"/v1/oauth/token": {
|
||||
"post": {
|
||||
"operationId": "token",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "grant_type",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": { "enum": ["authorization_code", "refresh_token"], "type": "string" }
|
||||
},
|
||||
{ "name": "client_id", "required": true, "in": "query", "schema": { "type": "string" } },
|
||||
{
|
||||
"name": "client_secret",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
{ "name": "code", "required": false, "in": "query", "schema": { "type": "string" } },
|
||||
{
|
||||
"name": "code_verifier",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
{
|
||||
"name": "redirect_uri",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
{
|
||||
"name": "refresh_token",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": { "type": "string" }
|
||||
}
|
||||
],
|
||||
"responses": { "201": { "description": "" } },
|
||||
"tags": ["oauth (beta)"]
|
||||
}
|
||||
},
|
||||
"/v1/oauth/authorizations/{id}": {
|
||||
"get": {
|
||||
"operationId": "getAuthorizationRequest",
|
||||
"parameters": [
|
||||
{ "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "" } },
|
||||
"tags": ["oauth (beta)"],
|
||||
"security": [{ "bearer": [] }]
|
||||
},
|
||||
"post": {
|
||||
"operationId": "approveAuthorizationRequest",
|
||||
"parameters": [
|
||||
{ "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/AuthorizationsApproveBody" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": { "201": { "description": "" } },
|
||||
"tags": ["oauth (beta)"],
|
||||
"security": [{ "bearer": [] }]
|
||||
},
|
||||
"delete": {
|
||||
"operationId": "declineAuthorizationRequest",
|
||||
"parameters": [
|
||||
{ "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }
|
||||
],
|
||||
"responses": { "200": { "description": "" } },
|
||||
"tags": ["oauth (beta)"],
|
||||
"security": [{ "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/v1/projects/{ref}/functions": {
|
||||
"post": {
|
||||
"operationId": "createFunction",
|
||||
@@ -132,6 +252,18 @@
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": { "type": "boolean" }
|
||||
},
|
||||
{
|
||||
"name": "entrypoint_path",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
{
|
||||
"name": "import_map_path",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": { "type": "string" }
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
@@ -261,6 +393,18 @@
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": { "type": "boolean" }
|
||||
},
|
||||
{
|
||||
"name": "entrypoint_path",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": { "type": "string" }
|
||||
},
|
||||
{
|
||||
"name": "import_map_path",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": { "type": "string" }
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
@@ -344,6 +488,36 @@
|
||||
"security": [{ "apiKeyHeader": [], "apiKeyParam": [] }, { "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/v1/projects/{ref}/api-keys": {
|
||||
"get": {
|
||||
"operationId": "getProjectApiKeys",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "ref",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"description": "Project ref",
|
||||
"schema": { "minLength": 20, "maxLength": 20, "type": "string" }
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/components/schemas/ApiKeyResponse" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": { "description": "" }
|
||||
},
|
||||
"tags": ["projects"],
|
||||
"security": [{ "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/v1/projects/{ref}/custom-hostname": {
|
||||
"get": {
|
||||
"operationId": "getCustomHostnameConfig",
|
||||
@@ -615,7 +789,7 @@
|
||||
},
|
||||
"/v1/projects/{ref}/pgsodium": {
|
||||
"get": {
|
||||
"operationId": "getConfig",
|
||||
"operationId": "getPgsodiumConfig",
|
||||
"summary": "Gets project's pgsodium config",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -642,7 +816,7 @@
|
||||
"security": [{ "bearer": [] }]
|
||||
},
|
||||
"put": {
|
||||
"operationId": "updateConfig",
|
||||
"operationId": "updatePgsodiumConfig",
|
||||
"summary": "Updates project's pgsodium config. Updating the root_key can cause all data encrypted with the older key to become inaccessible.",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -695,7 +869,7 @@
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/PostgrestConfigResponse" }
|
||||
"schema": { "$ref": "#/components/schemas/PostgrestConfigWithJWTSecretResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1252,6 +1426,69 @@
|
||||
"security": [{ "bearer": [] }, { "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/v1/projects/{ref}/config/database/pgbouncer": {
|
||||
"get": {
|
||||
"operationId": "getPgbouncerConfig",
|
||||
"summary": "Gets project's pgbouncer config",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "ref",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"description": "Project ref",
|
||||
"schema": { "minLength": 20, "maxLength": 20, "type": "string" }
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ProjectPgBouncerConfig" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": { "description": "Failed to retrieve project's pgbouncer config" }
|
||||
},
|
||||
"tags": ["projects config"],
|
||||
"security": [{ "bearer": [] }, { "bearer": [] }]
|
||||
},
|
||||
"patch": {
|
||||
"operationId": "updatePgbouncerConfig",
|
||||
"summary": "Updates project's pgbouncer config",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "ref",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"description": "Project ref",
|
||||
"schema": { "minLength": 20, "maxLength": 20, "type": "string" }
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/UpdatePgbouncerConfigBody" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/UpdatePoolingConfigResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": { "description": "" },
|
||||
"500": { "description": "Failed to update project's pgbouncer config" }
|
||||
},
|
||||
"tags": ["projects config"],
|
||||
"security": [{ "bearer": [] }, { "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/v1/projects/{ref}/config/auth/sso/providers": {
|
||||
"post": {
|
||||
"operationId": "createProviderForProject",
|
||||
@@ -1500,6 +1737,11 @@
|
||||
"properties": { "name": { "type": "string" } },
|
||||
"required": ["name"]
|
||||
},
|
||||
"AuthorizationsApproveBody": {
|
||||
"type": "object",
|
||||
"properties": { "organization_id": { "type": "string" } },
|
||||
"required": ["organization_id"]
|
||||
},
|
||||
"CreateFunctionBody": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1521,7 +1763,9 @@
|
||||
"created_at": { "type": "number" },
|
||||
"updated_at": { "type": "number" },
|
||||
"verify_jwt": { "type": "boolean" },
|
||||
"import_map": { "type": "boolean" }
|
||||
"import_map": { "type": "boolean" },
|
||||
"entrypoint_path": { "type": "string" },
|
||||
"import_map_path": { "type": "string" }
|
||||
},
|
||||
"required": ["id", "slug", "name", "status", "version", "created_at", "updated_at"]
|
||||
},
|
||||
@@ -1536,7 +1780,9 @@
|
||||
"created_at": { "type": "number" },
|
||||
"updated_at": { "type": "number" },
|
||||
"verify_jwt": { "type": "boolean" },
|
||||
"import_map": { "type": "boolean" }
|
||||
"import_map": { "type": "boolean" },
|
||||
"entrypoint_path": { "type": "string" },
|
||||
"import_map_path": { "type": "string" }
|
||||
},
|
||||
"required": ["id", "slug", "name", "status", "version", "created_at", "updated_at"]
|
||||
},
|
||||
@@ -1548,6 +1794,11 @@
|
||||
"verify_jwt": { "type": "boolean" }
|
||||
}
|
||||
},
|
||||
"ApiKeyResponse": {
|
||||
"type": "object",
|
||||
"properties": { "name": { "type": "string" }, "api_key": { "type": "string" } },
|
||||
"required": ["name", "api_key"]
|
||||
},
|
||||
"UpdateCustomHostnameResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1608,12 +1859,13 @@
|
||||
"properties": { "root_key": { "type": "string" } },
|
||||
"required": ["root_key"]
|
||||
},
|
||||
"PostgrestConfigResponse": {
|
||||
"PostgrestConfigWithJWTSecretResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"max_rows": { "type": "integer" },
|
||||
"db_schema": { "type": "string" },
|
||||
"db_extra_search_path": { "type": "string" }
|
||||
"db_extra_search_path": { "type": "string" },
|
||||
"jwt_secret": { "type": "string" }
|
||||
},
|
||||
"required": ["max_rows", "db_schema", "db_extra_search_path"]
|
||||
},
|
||||
@@ -1625,6 +1877,15 @@
|
||||
"db_schema": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"PostgrestConfigResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"max_rows": { "type": "integer" },
|
||||
"db_schema": { "type": "string" },
|
||||
"db_extra_search_path": { "type": "string" }
|
||||
},
|
||||
"required": ["max_rows", "db_schema", "db_extra_search_path"]
|
||||
},
|
||||
"SecretResponse": {
|
||||
"type": "object",
|
||||
"properties": { "name": { "type": "string" }, "value": { "type": "string" } },
|
||||
@@ -1832,12 +2093,101 @@
|
||||
"session_replication_role"
|
||||
]
|
||||
},
|
||||
"ProjectPgBouncerConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"db_dns_name": { "type": "string" },
|
||||
"db_host": { "type": "string" },
|
||||
"db_name": { "type": "string" },
|
||||
"db_port": { "type": "number" },
|
||||
"db_ssl": { "type": "boolean" },
|
||||
"db_user": { "type": "string" },
|
||||
"default_pool_size": { "type": "number" },
|
||||
"ignore_startup_parameters": { "type": "string" },
|
||||
"inserted_at": { "type": "string" },
|
||||
"pgbouncer_enabled": { "type": "boolean" },
|
||||
"pgbouncer_status": {
|
||||
"enum": ["COMING_DOWN", "COMING_UP", "DISABLED", "ENABLED", "RELOADING"],
|
||||
"type": "string"
|
||||
},
|
||||
"pool_mode": { "enum": ["transaction", "session", "statement"], "type": "string" },
|
||||
"max_client_conn": { "type": "number", "nullable": true }
|
||||
},
|
||||
"required": [
|
||||
"db_dns_name",
|
||||
"db_host",
|
||||
"db_name",
|
||||
"db_port",
|
||||
"db_ssl",
|
||||
"db_user",
|
||||
"default_pool_size",
|
||||
"ignore_startup_parameters",
|
||||
"inserted_at",
|
||||
"pgbouncer_enabled",
|
||||
"pgbouncer_status",
|
||||
"pool_mode"
|
||||
]
|
||||
},
|
||||
"UpdatePgbouncerConfigBody": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default_pool_size": { "type": "integer", "minimum": 0, "maximum": 65535 },
|
||||
"max_client_conn": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"minimum": 0,
|
||||
"maximum": 18000
|
||||
},
|
||||
"ignore_startup_parameters": { "type": "string" },
|
||||
"pgbouncer_enabled": { "type": "boolean" },
|
||||
"pool_mode": { "enum": ["transaction", "session", "statement"], "type": "string" }
|
||||
},
|
||||
"required": [
|
||||
"default_pool_size",
|
||||
"ignore_startup_parameters",
|
||||
"pgbouncer_enabled",
|
||||
"pool_mode"
|
||||
]
|
||||
},
|
||||
"UpdatePoolingConfigResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default_pool_size": { "type": "integer", "minimum": 0, "maximum": 65535 },
|
||||
"max_client_conn": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"minimum": 0,
|
||||
"maximum": 18000
|
||||
},
|
||||
"ignore_startup_parameters": { "type": "string" },
|
||||
"pgbouncer_enabled": { "type": "boolean" },
|
||||
"pool_mode": { "enum": ["transaction", "session", "statement"], "type": "string" },
|
||||
"pgbouncer_status": {
|
||||
"enum": ["COMING_DOWN", "COMING_UP", "DISABLED", "ENABLED", "RELOADING"],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"default_pool_size",
|
||||
"ignore_startup_parameters",
|
||||
"pgbouncer_enabled",
|
||||
"pool_mode",
|
||||
"pgbouncer_status"
|
||||
]
|
||||
},
|
||||
"AttributeValue": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default": {
|
||||
"oneOf": [
|
||||
{ "type": "object" },
|
||||
{ "type": "number" },
|
||||
{ "type": "string" },
|
||||
{ "type": "boolean" }
|
||||
]
|
||||
},
|
||||
"name": { "type": "string" },
|
||||
"names": { "type": "array", "items": { "type": "string" } },
|
||||
"default": { "type": "object" }
|
||||
"names": { "type": "array", "items": { "type": "string" } }
|
||||
}
|
||||
},
|
||||
"AttributeMapping": {
|
||||
|
||||
@@ -1107,7 +1107,7 @@ commands:
|
||||
summary: Authenticate using an access token
|
||||
description: |2
|
||||
|
||||
Connect the Supabase CLI to your Supabase account by logging in with your [personal access token](https://app.supabase.com/account/tokens).
|
||||
Connect the Supabase CLI to your Supabase account by logging in with your [personal access token](https://supabase.com/dashboard/account/tokens).
|
||||
|
||||
Your access token is stored securely in [native credentials storage](https://github.com/zalando/go-keyring#dependencies). If native credentials storage is unavailable, it will be written to a plain text file at `~/.supabase/access-token`.
|
||||
|
||||
@@ -1119,7 +1119,7 @@ commands:
|
||||
name: Basic usage
|
||||
code: supabase login
|
||||
response: |
|
||||
You can generate an access token from https://app.supabase.com/account/tokens
|
||||
You can generate an access token from https://supabase.com/dashboard/account/tokens
|
||||
Enter your access token: sbp_****************************************
|
||||
Finished supabase login.
|
||||
tags:
|
||||
|
||||
+5514
-4809
File diff suppressed because it is too large.
Load diff
+4965
-4588
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -80,26 +80,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 553,
|
||||
"name": "allowEmpty",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 545,
|
||||
"name": "body",
|
||||
@@ -243,6 +223,26 @@
|
||||
"name": "Record"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 553,
|
||||
"name": "isMaybeSingle",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 541,
|
||||
"name": "method",
|
||||
@@ -662,7 +662,7 @@
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [553, 545, 548, 543, 541, 544, 546, 547, 542]
|
||||
"children": [545, 548, 543, 553, 541, 544, 546, 547, 542]
|
||||
},
|
||||
{
|
||||
"title": "Methods",
|
||||
@@ -1991,31 +1991,6 @@
|
||||
"name": "default.constructor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 419,
|
||||
"name": "allowEmpty",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
},
|
||||
"inheritedFrom": {
|
||||
"type": "reference",
|
||||
"id": 518,
|
||||
"name": "default.allowEmpty"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 411,
|
||||
"name": "body",
|
||||
@@ -2174,6 +2149,31 @@
|
||||
"name": "default.headers"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 419,
|
||||
"name": "isMaybeSingle",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
},
|
||||
"inheritedFrom": {
|
||||
"type": "reference",
|
||||
"id": 518,
|
||||
"name": "default.isMaybeSingle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 407,
|
||||
"name": "method",
|
||||
@@ -2850,7 +2850,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 160,
|
||||
"line": 166,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -3062,7 +3062,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 194,
|
||||
"line": 200,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -3110,7 +3110,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 202,
|
||||
"line": 208,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -3133,7 +3133,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 205,
|
||||
"line": 211,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -3156,7 +3156,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 207,
|
||||
"line": 213,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -3188,7 +3188,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 204,
|
||||
"line": 210,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -3211,7 +3211,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 203,
|
||||
"line": 209,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -3234,7 +3234,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 206,
|
||||
"line": 212,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -3597,7 +3597,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 168,
|
||||
"line": 174,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -7946,7 +7946,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 246,
|
||||
"line": 252,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -8016,7 +8016,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 232,
|
||||
"line": 238,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -8920,7 +8920,7 @@
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [419, 411, 414, 409, 407, 410, 412, 413, 408]
|
||||
"children": [411, 414, 409, 419, 407, 410, 412, 413, 408]
|
||||
},
|
||||
{
|
||||
"title": "Methods",
|
||||
@@ -10583,31 +10583,6 @@
|
||||
"name": "default.constructor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 518,
|
||||
"name": "allowEmpty",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
},
|
||||
"inheritedFrom": {
|
||||
"type": "reference",
|
||||
"id": 553,
|
||||
"name": "default.allowEmpty"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 510,
|
||||
"name": "body",
|
||||
@@ -10766,6 +10741,31 @@
|
||||
"name": "default.headers"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 518,
|
||||
"name": "isMaybeSingle",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
},
|
||||
"inheritedFrom": {
|
||||
"type": "reference",
|
||||
"id": 553,
|
||||
"name": "default.isMaybeSingle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 506,
|
||||
"name": "method",
|
||||
@@ -10994,7 +10994,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 160,
|
||||
"line": 166,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -11031,7 +11031,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 194,
|
||||
"line": 200,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -11079,7 +11079,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 202,
|
||||
"line": 208,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -11102,7 +11102,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 205,
|
||||
"line": 211,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -11125,7 +11125,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 207,
|
||||
"line": 213,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -11157,7 +11157,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 204,
|
||||
"line": 210,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -11180,7 +11180,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 203,
|
||||
"line": 209,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -11203,7 +11203,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 206,
|
||||
"line": 212,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -11279,7 +11279,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 168,
|
||||
"line": 174,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -11984,7 +11984,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 246,
|
||||
"line": 252,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -12044,7 +12044,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 232,
|
||||
"line": 238,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -12584,7 +12584,7 @@
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [518, 510, 513, 508, 506, 509, 511, 512, 507]
|
||||
"children": [510, 513, 508, 518, 506, 509, 511, 512, 507]
|
||||
},
|
||||
{
|
||||
"title": "Methods",
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [553, 545, 548, 543, 541, 544, 546, 547, 542]
|
||||
"children": [545, 548, 543, 553, 541, 544, 546, 547, 542]
|
||||
},
|
||||
{
|
||||
"title": "Methods",
|
||||
@@ -152,26 +152,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 553,
|
||||
"name": "allowEmpty",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 545,
|
||||
"name": "body",
|
||||
@@ -315,6 +295,26 @@
|
||||
"name": "Record"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 553,
|
||||
"name": "isMaybeSingle",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 541,
|
||||
"name": "method",
|
||||
@@ -734,7 +734,7 @@
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [553, 545, 548, 543, 541, 544, 546, 547, 542]
|
||||
"children": [545, 548, 543, 553, 541, 544, 546, 547, 542]
|
||||
},
|
||||
{
|
||||
"title": "Methods",
|
||||
@@ -2105,26 +2105,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 553,
|
||||
"name": "allowEmpty",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 545,
|
||||
"name": "body",
|
||||
@@ -2268,6 +2248,26 @@
|
||||
"name": "Record"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 553,
|
||||
"name": "isMaybeSingle",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 541,
|
||||
"name": "method",
|
||||
@@ -2687,7 +2687,7 @@
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [553, 545, 548, 543, 541, 544, 546, 547, 542]
|
||||
"children": [545, 548, 543, 553, 541, 544, 546, 547, 542]
|
||||
},
|
||||
{
|
||||
"title": "Methods",
|
||||
@@ -2779,31 +2779,6 @@
|
||||
"name": "default.constructor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 419,
|
||||
"name": "allowEmpty",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
},
|
||||
"inheritedFrom": {
|
||||
"type": "reference",
|
||||
"id": 518,
|
||||
"name": "default.allowEmpty"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 411,
|
||||
"name": "body",
|
||||
@@ -2962,6 +2937,31 @@
|
||||
"name": "default.headers"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 419,
|
||||
"name": "isMaybeSingle",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
},
|
||||
"inheritedFrom": {
|
||||
"type": "reference",
|
||||
"id": 518,
|
||||
"name": "default.isMaybeSingle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 407,
|
||||
"name": "method",
|
||||
@@ -3640,7 +3640,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 160,
|
||||
"line": 166,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -3853,7 +3853,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 194,
|
||||
"line": 200,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -3901,7 +3901,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 202,
|
||||
"line": 208,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -3924,7 +3924,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 205,
|
||||
"line": 211,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -3947,7 +3947,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 207,
|
||||
"line": 213,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -3979,7 +3979,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 204,
|
||||
"line": 210,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -4002,7 +4002,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 203,
|
||||
"line": 209,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -4025,7 +4025,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 206,
|
||||
"line": 212,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -4389,7 +4389,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 168,
|
||||
"line": 174,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -8759,7 +8759,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 246,
|
||||
"line": 252,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -8829,7 +8829,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 232,
|
||||
"line": 238,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -9735,7 +9735,7 @@
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [419, 411, 414, 409, 407, 410, 412, 413, 408]
|
||||
"children": [411, 414, 409, 419, 407, 410, 412, 413, 408]
|
||||
},
|
||||
{
|
||||
"title": "Methods",
|
||||
@@ -11437,26 +11437,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 553,
|
||||
"name": "allowEmpty",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 545,
|
||||
"name": "body",
|
||||
@@ -11600,6 +11580,26 @@
|
||||
"name": "Record"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 553,
|
||||
"name": "isMaybeSingle",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 541,
|
||||
"name": "method",
|
||||
@@ -12019,7 +12019,7 @@
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [553, 545, 548, 543, 541, 544, 546, 547, 542]
|
||||
"children": [545, 548, 543, 553, 541, 544, 546, 547, 542]
|
||||
},
|
||||
{
|
||||
"title": "Methods",
|
||||
@@ -12111,31 +12111,6 @@
|
||||
"name": "default.constructor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 518,
|
||||
"name": "allowEmpty",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
},
|
||||
"inheritedFrom": {
|
||||
"type": "reference",
|
||||
"id": 553,
|
||||
"name": "default.allowEmpty"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 510,
|
||||
"name": "body",
|
||||
@@ -12294,6 +12269,31 @@
|
||||
"name": "default.headers"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 518,
|
||||
"name": "isMaybeSingle",
|
||||
"kind": 1024,
|
||||
"kindString": "Property",
|
||||
"flags": {
|
||||
"isProtected": true
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestBuilder.ts",
|
||||
"line": 16,
|
||||
"character": 12
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"type": "intrinsic",
|
||||
"name": "boolean"
|
||||
},
|
||||
"inheritedFrom": {
|
||||
"type": "reference",
|
||||
"id": 553,
|
||||
"name": "default.isMaybeSingle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 506,
|
||||
"name": "method",
|
||||
@@ -12522,7 +12522,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 160,
|
||||
"line": 166,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -12559,7 +12559,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 194,
|
||||
"line": 200,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -12607,7 +12607,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 202,
|
||||
"line": 208,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -12630,7 +12630,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 205,
|
||||
"line": 211,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -12653,7 +12653,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 207,
|
||||
"line": 213,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -12685,7 +12685,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 204,
|
||||
"line": 210,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -12708,7 +12708,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 203,
|
||||
"line": 209,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -12731,7 +12731,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 206,
|
||||
"line": 212,
|
||||
"character": 4
|
||||
}
|
||||
],
|
||||
@@ -12807,7 +12807,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 168,
|
||||
"line": 174,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -13513,7 +13513,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 246,
|
||||
"line": 252,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -13573,7 +13573,7 @@
|
||||
"sources": [
|
||||
{
|
||||
"fileName": "src/PostgrestTransformBuilder.ts",
|
||||
"line": 232,
|
||||
"line": 238,
|
||||
"character": 2
|
||||
}
|
||||
],
|
||||
@@ -14114,7 +14114,7 @@
|
||||
{
|
||||
"title": "Properties",
|
||||
"kind": 1024,
|
||||
"children": [518, 510, 513, 508, 506, 509, 511, 512, 507]
|
||||
"children": [510, 513, 508, 518, 506, 509, 511, 512, 507]
|
||||
},
|
||||
{
|
||||
"title": "Methods",
|
||||
|
||||
@@ -114,13 +114,13 @@ functions:
|
||||
title: 'signUp()'
|
||||
$ref: '@supabase/gotrue-js.GoTrueClient.signUp'
|
||||
notes: |
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://app.supabase.com/project/_/auth/providers).
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- By default, when the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://app.supabase.com/project/_/auth/url-configuration).
|
||||
- By default, when the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- If signUp() is called for an existing confirmed user:
|
||||
- If **Confirm email** is enabled in [your project](https://app.supabase.com/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is enabled in [your project](https://supabase.com/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is disabled, the error message, `User already registered` is returned.
|
||||
- To fetch the currently logged-in user, refer to [`getUser()`](/docs/reference/javascript/auth-getuser).
|
||||
examples:
|
||||
@@ -192,7 +192,7 @@ functions:
|
||||
- This method is used for passwordless sign-ins where a OTP is sent to the user's email or phone number.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). You can modify the `SITE_URL` or add additional redirect URLs in [your project](https://app.supabase.com/project/_/auth/url-configuration).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). You can modify the `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
examples:
|
||||
- id: sign-in-with-email.
|
||||
name: Sign in with email.
|
||||
@@ -236,7 +236,7 @@ functions:
|
||||
isSpotlight: false
|
||||
description: |
|
||||
When the third-party provider successfully authenticates the user, the provider will redirect the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). It does not redirect the user immediately after invoking this method.
|
||||
You can modify the `SITE_URL` or add additional redirect URLs in [your project](https://app.supabase.com/project/_/auth/url-configuration).
|
||||
You can modify the `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
js: |
|
||||
```js
|
||||
const { data, error } = await supabase.auth.signInWithOAuth({
|
||||
@@ -334,7 +334,7 @@ functions:
|
||||
notes: |
|
||||
- In order to use the `updateUser()` method, the user needs to be signed in first.
|
||||
- By Default, email updates sends a confirmation link to both the user's current and new email.
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://app.supabase.com/project/_/auth/providers).
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
examples:
|
||||
- id: update-the-email-for-an-authenticated-user
|
||||
name: Update the email for an authenticated user
|
||||
@@ -746,7 +746,7 @@ functions:
|
||||
description: 'This is the description'
|
||||
$ref: '@supabase/postgrest-js.PostgrestQueryBuilder.select'
|
||||
notes: |
|
||||
- By default, Supabase projects return a maximum of 1,000 rows. This setting can be changed in your project's [API settings](https://app.supabase.com/project/_/settings/api). It's recommended that you keep it low to limit the payload size of accidental or malicious requests. You can use `range()` queries to paginate through your data.
|
||||
- By default, Supabase projects return a maximum of 1,000 rows. This setting can be changed in your project's [API settings](https://supabase.com/dashboard/project/_/settings/api). It's recommended that you keep it low to limit the payload size of accidental or malicious requests. You can use `range()` queries to paginate through your data.
|
||||
- `select()` can be combined with [Filters](/docs/reference/javascript/using-filters)
|
||||
- `select()` can be combined with [Modifiers](/docs/reference/javascript/using-modifiers)
|
||||
- `apikey` is a reserved keyword if you're using the [Supabase Platform](/docs/guides/platform) and [should be avoided as a column name](https://github.com/supabase/supabase/issues/5465).
|
||||
@@ -5406,7 +5406,7 @@ functions:
|
||||
title: 'from.getPublicUrl()'
|
||||
$ref: '@supabase/storage-js.packages/StorageFileApi.default.getPublicUrl'
|
||||
notes: |
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://app.supabase.com), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- RLS policy permissions required:
|
||||
- `buckets` table permissions: none
|
||||
- `objects` table permissions: none
|
||||
|
||||
@@ -555,6 +555,7 @@
|
||||
"description": "Successful response",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Id": { "type": "string" },
|
||||
"Key": { "type": "string", "examples": ["avatars/folder/cat.png"] }
|
||||
},
|
||||
"required": ["Key"]
|
||||
@@ -607,6 +608,7 @@
|
||||
"description": "Successful response",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Id": { "type": "string" },
|
||||
"Key": { "type": "string", "examples": ["avatars/folder/cat.png"] }
|
||||
},
|
||||
"required": ["Key"]
|
||||
|
||||
@@ -119,13 +119,13 @@ functions:
|
||||
description: |
|
||||
Creates a new user.
|
||||
notes: |
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://app.supabase.com/project/_/auth/providers).
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://app.supabase.com/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- If SignUp() is called for an existing confirmed user:
|
||||
- If **Confirm email** is enabled in [your project](https://app.supabase.com/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is enabled in [your project](https://supabase.com/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is disabled, the error message, `User already registered` is returned.
|
||||
examples:
|
||||
- id: sign-up
|
||||
@@ -162,7 +162,7 @@ functions:
|
||||
- This method is used for passwordless sign-ins where a OTP is sent to the user's email or phone number.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). You can modify the `SITE_URL` or add additional redirect urls in [your project](https://app.supabase.com/project/_/auth/settings).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). You can modify the `SITE_URL` or add additional redirect urls in [your project](https://supabase.com/dashboard/project/_/auth/settings).
|
||||
examples:
|
||||
- id: sign-in-with-email
|
||||
name: Send Magic Link.
|
||||
@@ -269,7 +269,7 @@ functions:
|
||||
notes: |
|
||||
- In order to use the `UpdateUser()` method, the user needs to be signed in first.
|
||||
- By Default, email updates sends a confirmation link to both the user's current and new email.
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://app.supabase.com/project/_/auth/settings).
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://supabase.com/dashboard/project/_/auth/settings).
|
||||
examples:
|
||||
- id: update-the-email-for-an-authenticated-user
|
||||
name: Update the email for an authenticated user
|
||||
@@ -1055,7 +1055,7 @@ functions:
|
||||
Retrieve URLs for assets in public buckets
|
||||
title: 'from.getPublicUrl()'
|
||||
notes: |
|
||||
- The bucket needs to be set to public, either via [UpdateBucket()](/docs/reference/csharp/storage-updatebucket) or by going to Storage on [app.supabase.com](https://app.supabase.com), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- The bucket needs to be set to public, either via [UpdateBucket()](/docs/reference/csharp/storage-updatebucket) or by going to Storage on [app.supabase.com](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- Policy permissions required:
|
||||
- `buckets` permissions: none
|
||||
- `objects` permissions: none
|
||||
|
||||
@@ -78,10 +78,10 @@ functions:
|
||||
description: |
|
||||
Creates a new user.
|
||||
notes: |
|
||||
- By default, the user will need to verify their email address before logging in. If you would like to change this, you can disable "Email Confirmations" by going to Authentication -> Settings on [app.supabase.com](https://app.supabase.com)
|
||||
- By default, the user will need to verify their email address before logging in. If you would like to change this, you can disable "Email Confirmations" by going to Authentication -> Settings on [app.supabase.com](https://supabase.com/dashboard)
|
||||
- If "Email Confirmations" is turned on, a user is returned but session will be null
|
||||
- If "Email Confirmations" is turned off, both a `user` and a `session` will be returned
|
||||
- When the user confirms their email address, they will be redirected to localhost:3000 by default. To change this, you can go to Authentication -> Settings on [app.supabase.com](https://app.supabase.com)
|
||||
- When the user confirms their email address, they will be redirected to localhost:3000 by default. To change this, you can go to Authentication -> Settings on [app.supabase.com](https://supabase.com/dashboard)
|
||||
examples:
|
||||
- id: sign-up
|
||||
name: Sign up.
|
||||
@@ -106,7 +106,7 @@ functions:
|
||||
notes: |
|
||||
- A user can sign up via email, phone number.
|
||||
- If you provide `email` without a `password`, the user will be sent a magic link.
|
||||
- The magic link's destination URL is determined by the SITE_URL config variable. To change this, you can go to Authentication -> Settings on [app.supabase.com](https://app.supabase.com)
|
||||
- The magic link's destination URL is determined by the SITE_URL config variable. To change this, you can go to Authentication -> Settings on [app.supabase.com](https://supabase.com/dashboard)
|
||||
- Similarly, if you provide `phone` without a `password`, the user will be sent a one time password.
|
||||
- If you are looking to sign users in with OAuth in Flutter apps, go to [`signInWithProvider()`](/docs/reference/dart/v0/auth-signinwithprovider).
|
||||
examples:
|
||||
@@ -1073,7 +1073,7 @@ functions:
|
||||
Retrieve URLs for assets in public buckets
|
||||
title: 'from.getPublicUrl()'
|
||||
notes: |
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://app.supabase.com), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- Policy permissions required:
|
||||
- `buckets` permissions: none
|
||||
- `objects` permissions: none
|
||||
|
||||
@@ -56,13 +56,13 @@ functions:
|
||||
description: |
|
||||
Creates a new user.
|
||||
notes: |
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://app.supabase.com/project/_/auth/providers).
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://app.supabase.com/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- If signUp() is called for an existing confirmed user:
|
||||
- If **Confirm email** is enabled in [your project](https://app.supabase.com/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is enabled in [your project](https://supabase.com/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is disabled, the error message, `User already registered` is returned.
|
||||
examples:
|
||||
- id: sign-up
|
||||
@@ -141,7 +141,7 @@ functions:
|
||||
- This method is used for passwordless sign-ins where a OTP is sent to the user's email or phone number.
|
||||
- If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP.
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). You can modify the `SITE_URL` or add additional redirect urls in [your project](https://app.supabase.com/project/_/auth/url-configuration).
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). You can modify the `SITE_URL` or add additional redirect urls in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
examples:
|
||||
- id: sign-in-with-email
|
||||
name: Sign in with email.
|
||||
@@ -301,7 +301,7 @@ functions:
|
||||
notes: |
|
||||
- In order to use the `updateUser()` method, the user needs to be signed in first.
|
||||
- By Default, email updates sends a confirmation link to both the user's current and new email.
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://app.supabase.com/project/_/auth/providers).
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
examples:
|
||||
- id: update-the-email-for-an-authenticated-user
|
||||
name: Update the email for an authenticated user
|
||||
@@ -1215,7 +1215,7 @@ functions:
|
||||
Retrieve URLs for assets in public buckets
|
||||
title: 'from.getPublicUrl()'
|
||||
notes: |
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://app.supabase.com), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- Policy permissions required:
|
||||
- `buckets` permissions: none
|
||||
- `objects` permissions: none
|
||||
|
||||
@@ -82,10 +82,10 @@ functions:
|
||||
title: 'signUp()'
|
||||
$ref: '@supabase/gotrue-js.GoTrueClient.signUp'
|
||||
notes: |
|
||||
- By default, the user will need to verify their email address before logging in. If you would like to change this, you can disable "Email Confirmations" by going to Authentication -> Settings on [app.supabase.com](https://app.supabase.com)
|
||||
- By default, the user will need to verify their email address before logging in. If you would like to change this, you can disable "Email Confirmations" by going to Authentication -> Settings on [app.supabase.com](https://supabase.com/dashboard)
|
||||
- If "Email Confirmations" is turned on, a `user` is returned but `session` will be null
|
||||
- If "Email Confirmations" is turned off, both a `user` and a `session` will be returned
|
||||
- When the user confirms their email address, they will be redirected to localhost:3000 by default. To change this, you can go to Authentication -> Settings on [app.supabase.com](https://app.supabase.com)
|
||||
- When the user confirms their email address, they will be redirected to localhost:3000 by default. To change this, you can go to Authentication -> Settings on [app.supabase.com](https://supabase.com/dashboard)
|
||||
- If signUp() is called for an existing confirmed user:
|
||||
- If "Enable email confirmations" is enabled on the "Authentication" -> "Settings" page, an obfuscated / fake user object will be returned.
|
||||
- If "Enable email confirmations" is disabled, an error with a message "User already registered" will be returned.
|
||||
@@ -147,7 +147,7 @@ functions:
|
||||
notes: |
|
||||
- A user can sign up either via email or OAuth.
|
||||
- If you provide `email` without a `password`, the user will be sent a magic link.
|
||||
- The magic link's destination URL is determined by the SITE_URL config variable. To change this, you can go to Authentication -> Settings on [app.supabase.com](https://app.supabase.com)
|
||||
- The magic link's destination URL is determined by the SITE_URL config variable. To change this, you can go to Authentication -> Settings on [app.supabase.com](https://supabase.com/dashboard)
|
||||
- Specifying a `provider` will open the browser to the relevant login page.
|
||||
examples:
|
||||
- id: sign-in-with-email-and-password
|
||||
@@ -196,7 +196,7 @@ functions:
|
||||
the Auth server. If you are using email/phone logins you should set up your own redirects (within the email/sms template).
|
||||
|
||||
Sometimes you want to control where the user is redirected to after they are logged in. Supabase supports this for
|
||||
any URL path on your website (the URL must either be on the same domain as your [Site URL](https://app.supabase.com/project/_/auth/url-configuration) or match one of the Redirect URLs).
|
||||
any URL path on your website (the URL must either be on the same domain as your [Site URL](https://supabase.com/dashboard/project/_/auth/url-configuration) or match one of the Redirect URLs).
|
||||
|
||||
See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
code: |
|
||||
@@ -285,7 +285,7 @@ functions:
|
||||
$ref: '@supabase/gotrue-js.GoTrueClient.update'
|
||||
notes: |
|
||||
User email: By Default, email updates sends a confirmation link to both the user's current and new email.
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://app.supabase.com/project/_/auth/providers).
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
|
||||
User metadata: It's generally better to store user data in a table within your public schema (i.e., `public.users`).
|
||||
Use the `update()` method if you have data which rarely changes or is specific only to the logged in user.
|
||||
@@ -1521,7 +1521,7 @@ functions:
|
||||
title: 'from.getPublicUrl()'
|
||||
$ref: '@supabase/storage-js."lib/StorageFileApi".StorageFileApi.getPublicUrl'
|
||||
notes: |
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://app.supabase.com), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- Policy permissions required:
|
||||
- `buckets` permissions: none
|
||||
- `objects` permissions: none
|
||||
|
||||
@@ -68,7 +68,7 @@ functions:
|
||||
```
|
||||
description: |
|
||||
By default the API server points to the `public` schema. You can enable other database schemas within the Dashboard.
|
||||
Go to [Settings > API > Exposed schemas](https://app.supabase.com/project/_/settings/api) and add the schema which you want to expose to the API.
|
||||
Go to [Settings > API > Exposed schemas](https://supabase.com/dashboard/project/_/settings/api) and add the schema which you want to expose to the API.
|
||||
|
||||
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.
|
||||
- id: custom-fetch-implementation
|
||||
@@ -136,13 +136,13 @@ functions:
|
||||
title: 'signUp()'
|
||||
$ref: '@supabase/gotrue-js.GoTrueClient.signUp'
|
||||
notes: |
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://app.supabase.com/project/_/auth/providers).
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://app.supabase.com/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- If signUp() is called for an existing confirmed user:
|
||||
- If **Confirm email** is enabled in [your project](https://app.supabase.com/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is enabled in [your project](https://supabase.com/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is disabled, the error message, `User already registered` is returned.
|
||||
- To fetch the currently logged-in user, refer to [`getUser()`](/docs/reference/javascript/auth-getuser).
|
||||
examples:
|
||||
@@ -233,7 +233,7 @@ functions:
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/reference/auth/config#site_url).
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://app.supabase.com/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://supabase.com/dashboard/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
- See our [Twilio Phone Auth Guide](/docs/guides/auth/phone-login/twilio) for details about configuring WhatsApp sign in.
|
||||
examples:
|
||||
- id: sign-in-with-email
|
||||
@@ -439,7 +439,7 @@ functions:
|
||||
notes: |
|
||||
- In order to use the `updateUser()` method, the user needs to be signed in first.
|
||||
- By Default, email updates sends a confirmation link to both the user's current and new email.
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://app.supabase.com/project/_/auth/providers).
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
examples:
|
||||
- id: update-the-email-for-an-authenticated-user
|
||||
name: Update the email for an authenticated user
|
||||
@@ -1033,7 +1033,7 @@ functions:
|
||||
title: 'Fetch data: select()'
|
||||
$ref: '@supabase/postgrest-js.PostgrestQueryBuilder.select'
|
||||
notes: |
|
||||
- By default, Supabase projects return a maximum of 1,000 rows. This setting can be changed in your project's [API settings](https://app.supabase.com/project/_/settings/api). It's recommended that you keep it low to limit the payload size of accidental or malicious requests. You can use `range()` queries to paginate through your data.
|
||||
- By default, Supabase projects return a maximum of 1,000 rows. This setting can be changed in your project's [API settings](https://supabase.com/dashboard/project/_/settings/api). It's recommended that you keep it low to limit the payload size of accidental or malicious requests. You can use `range()` queries to paginate through your data.
|
||||
- `select()` can be combined with [Filters](/docs/reference/javascript/using-filters)
|
||||
- `select()` can be combined with [Modifiers](/docs/reference/javascript/using-modifiers)
|
||||
- `apikey` is a reserved keyword if you're using the [Supabase Platform](/docs/guides/platform) and [should be avoided as a column name](https://github.com/supabase/supabase/issues/5465).
|
||||
@@ -4801,7 +4801,7 @@ functions:
|
||||
title: from.getPublicUrl()
|
||||
$ref: '@supabase/storage-js.packages/StorageFileApi.default.getPublicUrl'
|
||||
notes: |
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://app.supabase.com), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- RLS policy permissions required:
|
||||
- `buckets` table permissions: none
|
||||
- `objects` table permissions: none
|
||||
|
||||
@@ -2040,11 +2040,11 @@ functions:
|
||||
$ref: '@supabase/gotrue-js.GoTrueClient.signUp'
|
||||
notes: |
|
||||
Creates a new user.
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://app.supabase.com/project/_/auth/providers).
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, the return value is null and you will be logged in instead.
|
||||
- If **Confirm email** is disabled, the return value is the user and you won't be logged in automatically.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://app.supabase.com/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- [supabase-kt](https://github.com/supabase-community/supabase-kt/) has default platform implementations:
|
||||
- On JVM, you can currently only verify a sign up through the token. To send the token, rather than a redirect url, you have to change `{{ .ConfirmationURL }}` in your sign up email to `{{ .Token }}`
|
||||
- [deep links](https://developer.android.com/training/app-links/deep-linking) (Android),
|
||||
@@ -2052,7 +2052,7 @@ functions:
|
||||
- To enable deeplinking/url scheme support on Android/IOS set the `host` and the `scheme` in the GoTrue Config and call `client.handleDeeplinks(intent)` (Android) and `client.handleDeeplinks(url)` (IOS) on startup
|
||||
- the website origin (JS)
|
||||
- If signUpWith() is called for an existing confirmed user:
|
||||
- If **Confirm email** is enabled in [your project](https://app.supabase.com/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is enabled in [your project](https://supabase.com/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is disabled, the error message, `User already registered` is returned.
|
||||
examples:
|
||||
- id: sign-up-phone
|
||||
@@ -2161,7 +2161,7 @@ functions:
|
||||
- [url schemes](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app) (IOS)
|
||||
- To enable deeplinking/url scheme support on Android/IOS set the `host` and the `scheme` in the GoTrue Config and call `client.handleDeeplinks(intent)` (Android) and `client.handleDeeplinks(url)` (IOS) on startup
|
||||
- the website origin (JS)
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://app.supabase.com/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://supabase.com/dashboard/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
examples:
|
||||
- id: sign-in-with-email
|
||||
name: Sign in with email
|
||||
@@ -2368,7 +2368,7 @@ functions:
|
||||
Modifies the user data.
|
||||
- In order to use the `modifyUser()` method, the user needs to be signed in first.
|
||||
- By default, email updates sends a confirmation link to both the user's current and new email.
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://app.supabase.com/project/_/auth/providers).
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
examples:
|
||||
- id: update-the-email-for-an-authenticated-user
|
||||
name: Update the email for an authenticated user
|
||||
|
||||
@@ -50,13 +50,13 @@ functions:
|
||||
- id: sign-up
|
||||
title: 'sign_up()'
|
||||
notes: |
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://app.supabase.com/project/_/auth/providers).
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- By default, when the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://app.supabase.com/project/_/auth/url-configuration).
|
||||
- By default, when the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url). You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- If sign_up() is called for an existing confirmed user:
|
||||
- If **Confirm email** is enabled in [your project](https://app.supabase.com/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is enabled in [your project](https://supabase.com/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is disabled, the error message, `User already registered` is returned.
|
||||
- To fetch the currently logged-in user, refer to [`getUser()`](/docs/reference/javascript/auth-getuser).
|
||||
examples:
|
||||
@@ -118,7 +118,7 @@ functions:
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/reference/auth/config#site_url).
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://app.supabase.com/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://supabase.com/dashboard/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
examples:
|
||||
- id: sign-in-with-email
|
||||
name: Sign in with email
|
||||
@@ -274,7 +274,7 @@ functions:
|
||||
- id: select
|
||||
title: 'Fetch data: select()'
|
||||
notes: |
|
||||
- By default, Supabase projects return a maximum of 1,000 rows. This setting can be changed in your project's [API settings](https://app.supabase.com/project/_/settings/api). It's recommended that you keep it low to limit the payload size of accidental or malicious requests.
|
||||
- By default, Supabase projects return a maximum of 1,000 rows. This setting can be changed in your project's [API settings](https://supabase.com/dashboard/project/_/settings/api). It's recommended that you keep it low to limit the payload size of accidental or malicious requests.
|
||||
- `apikey` is a reserved keyword if you're using the [Supabase Platform](/docs/guides/platform) and [should be avoided as a column name](https://github.com/supabase/supabase/issues/5465).
|
||||
examples:
|
||||
- id: getting-your-data
|
||||
@@ -782,7 +782,7 @@ functions:
|
||||
- id: from-get-public-url
|
||||
title: 'from_.get_public_url()'
|
||||
notes: |
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://app.supabase.com), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
|
||||
- RLS policy permissions required:
|
||||
- `buckets` table permissions: none
|
||||
- `objects` table permissions: none
|
||||
|
||||
@@ -1985,13 +1985,13 @@ functions:
|
||||
title: 'signUp()'
|
||||
$ref: '@supabase/gotrue-js.GoTrueClient.signUp'
|
||||
notes: |
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://app.supabase.com/project/_/auth/providers).
|
||||
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
- **Confirm email** determines if users need to confirm their email address after signing up.
|
||||
- If **Confirm email** is enabled, a `user` is returned but `session` is null.
|
||||
- If **Confirm email** is disabled, both a `user` and a `session` are returned.
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://app.supabase.com/project/_/auth/url-configuration).
|
||||
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
|
||||
- If signUp() is called for an existing confirmed user:
|
||||
- If **Confirm email** is enabled in [your project](https://app.supabase.com/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is enabled in [your project](https://supabase.com/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- If **Confirm email** is disabled, the error message, `User already registered` is returned.
|
||||
- To fetch the currently logged-in user, refer to [`getUser()`](/docs/reference/javascript/auth-getuser).
|
||||
examples:
|
||||
@@ -2065,7 +2065,7 @@ functions:
|
||||
- If you're using phone, you can configure whether you want the user to receive a OTP.
|
||||
- The magic link's destination URL is determined by the [`SITE_URL`](/docs/reference/auth/config#site_url).
|
||||
- See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://app.supabase.com/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
- Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://supabase.com/dashboard/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`.
|
||||
examples:
|
||||
- id: sign-in-with-email
|
||||
name: Sign in with email
|
||||
@@ -2209,7 +2209,7 @@ functions:
|
||||
notes: |
|
||||
- In order to use the `updateUser()` method, the user needs to be signed in first.
|
||||
- By default, email updates sends a confirmation link to both the user's current and new email.
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://app.supabase.com/project/_/auth/providers).
|
||||
To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://supabase.com/dashboard/project/_/auth/providers).
|
||||
examples:
|
||||
- id: update-the-email-for-an-authenticated-user
|
||||
name: Update the email for an authenticated user
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 207 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user