Merge pull request #14589 from supabase/master

Prod deploy dashboard
This commit is contained in:
Alaister Young authored and GitHub committed 2023-05-25 17:19:48 +10:00
commit 3091a3749d
207 files changed
+40176 -17725

No files matched your search

+3
View File
@@ -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: ./
+3
View File
@@ -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
View File
@@ -1 +1 @@
^./i18n
^./i18n
+1 -1
View File
@@ -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"
>
+8 -4
View File
@@ -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
+5
View File
@@ -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.
+1
View File
@@ -16,6 +16,7 @@ Chase Granberry
Chris Copplestone
Dave Wilson
Div Arora
Divit D
Egor Romanov
Fabrizio Fenoglio
Francesco Sansalvadore
+2 -2
View File
@@ -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>
+1 -1
View File
@@ -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;
+24 -1
View File
@@ -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.
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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 -3
View File
@@ -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>
+3 -3
View File
@@ -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
+10
View File
@@ -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',
},
]
+5
View File
@@ -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'
+4 -4
View File
@@ -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
"
+13 -7
View File
@@ -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&apos;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
+1 -1
View File
@@ -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>
+27 -1
View File
@@ -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',
},
},
},
})
+1 -1
View File
@@ -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 -1
View File
@@ -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
+22 -23
View File
@@ -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} />
}
+21 -20
View File
@@ -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)
}
+10 -12
View File
@@ -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>
);
)
}
+56
View File
@@ -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>
</>
)
}
+10 -28
View File
@@ -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} />
}
+13 -14
View File
@@ -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>
);
)
}
+9 -15
View File
@@ -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 ?? []} />
</>
);
)
}
+27 -25
View File
@@ -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;
};
+8 -17
View File
@@ -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 -1
View File
@@ -1,7 +1,7 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
serverActions: true,
},
}
+753 -454
View File
File diff suppressed because it is too large. Load diff
+12 -10
View File
@@ -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 -1
View File
@@ -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;
};
};
}
+12641 -58
View File
File diff suppressed because it is too large. Load diff
-1
View File
@@ -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"
}
+162 -47
View File
@@ -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
+16
View File
@@ -0,0 +1,16 @@
{
"sourceType": "unambiguous",
"presets": [
[
"@babel/preset-env",
{
"targets": {
"chrome": 100
}
}
],
"@babel/preset-typescript",
"@babel/preset-react"
],
"plugins": []
}
+21 -39
View File
@@ -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: {},
},
}
+5 -1
View File
@@ -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;
}
+38
View File
@@ -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",
```
+57
View File
@@ -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%;
}
+58
View File
@@ -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%;
}
+166
View File
@@ -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%)"
}
}
-67
View File
@@ -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()
+25 -7
View File
@@ -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 />,
+2
View File
@@ -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;
}
+50 -34
View File
@@ -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>
)
+9 -11
View File
@@ -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
+29 -1
View File
@@ -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
View File
@@ -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": {
+2 -2
View File
@@ -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:
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+95 -95
View File
@@ -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",
+8 -8
View File
@@ -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
+2
View File
@@ -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"]
+6 -6
View File
@@ -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
+4 -4
View File
@@ -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
+6 -6
View File
@@ -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
+6 -6
View File
@@ -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
+8 -8
View File
@@ -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
+5 -5
View File
@@ -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
+6 -6
View File
@@ -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
+5 -5
View File
@@ -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