diff --git a/README.md b/README.md
index 4c35921e1a5..4a6e2f223da 100644
--- a/README.md
+++ b/README.md
@@ -142,7 +142,7 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
= (props) => {
const StickyHeader: FC = ({ icon, ...props }) => {
const router = useRouter()
+
const { setActiveRefItem } = useNavigationMenuContext()
+ // we're serving search bots a different file (/crawlers/[...slug])
+ // and need to modify content to suit that
+ const isCrawlerPage = router.route.includes('/crawlers/[...slug]')
+
const { ref } = useInView({
threshold: 1,
rootMargin: '30% 0% -35% 0px',
@@ -99,21 +105,25 @@ const StickyHeader: FC = ({ icon, ...props }) => {
{icon && (
-
+
)}
-
- {props.title && {props.title}}
-
+ {isCrawlerPage ? (
+
{props.title}
+ ) : (
+
+ {props.title && {props.title}}
+
+ )}
)
}
diff --git a/apps/docs/layouts/tutorials/TutorialLayout.tsx b/apps/docs/layouts/tutorials/TutorialLayout.tsx
index b49391425b2..fc1a2fa8ea9 100644
--- a/apps/docs/layouts/tutorials/TutorialLayout.tsx
+++ b/apps/docs/layouts/tutorials/TutorialLayout.tsx
@@ -2,7 +2,6 @@ import { MDXProvider } from '@mdx-js/react'
import Head from 'next/head'
import { FC, useEffect, useState } from 'react'
import components from '~/components'
-import Footer from '~/components/Footer'
import SideBar from '~/components/Navigation/SideBar'
import TableOfContents from '~/components/TableOfContents'
diff --git a/apps/docs/lib/mdx/getConfig.tsx b/apps/docs/lib/mdx/getConfig.tsx
new file mode 100644
index 00000000000..8a014486212
--- /dev/null
+++ b/apps/docs/lib/mdx/getConfig.tsx
@@ -0,0 +1,20 @@
+// @ts-expect-error
+import specStorageV0 from '~/../../spec/storage_v0_config.yaml' assert { type: 'yml' }
+// @ts-expect-error
+import specRealtimeV0 from '~/../../spec/realtime_v0_config.yaml' assert { type: 'yml' }
+// @ts-expect-error
+import specAuthV1 from '~/../../spec/gotrue_v1_config.yaml' assert { type: 'yml' }
+
+function getStorageConfigV0() {
+ return { ...specStorageV0 }
+}
+
+function getRealtimeConfigV0() {
+ return { ...specRealtimeV0 }
+}
+
+function getAuthConfigV1() {
+ return { ...specAuthV1 }
+}
+
+export { getStorageConfigV0, getRealtimeConfigV0, getAuthConfigV1 }
diff --git a/apps/docs/lib/refGenerator/helpers.ts b/apps/docs/lib/refGenerator/helpers.ts
index ed8f18f5e4e..6af5b8eb539 100644
--- a/apps/docs/lib/refGenerator/helpers.ts
+++ b/apps/docs/lib/refGenerator/helpers.ts
@@ -1,4 +1,4 @@
-import { TsDoc } from '~/generator/legacy/definitions'
+import { TsDoc } from '../../generator/legacy/definitions'
import { values, mapValues } from 'lodash'
import { OpenAPIV3 } from 'openapi-types'
@@ -60,6 +60,8 @@ function recurseThroughParams(paramDefinition: any) {
children = dereferenced.children
} else if (dereferenced.type?.declaration?.children) {
children = dereferenced.type.declaration.children
+ } else if (dereferenced.type?.type === 'query') {
+ // skip: ignore types created from `typeof` for now, like `type Fetch = typeof fetch`
} else if (dereferenced.type?.type === 'union') {
// skip: we don't want to show unions as nested parameters
} else if (Object.keys(dereferenced).length === 0) {
diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs
index 9762c179622..bf98d6d34fd 100644
--- a/apps/docs/next.config.mjs
+++ b/apps/docs/next.config.mjs
@@ -48,7 +48,12 @@ const nextConfig = {
basePath: '/docs',
images: {
dangerouslyAllowSVG: true,
- domains: ['avatars.githubusercontent.com', 'github.com', 'user-images.githubusercontent.com'],
+ domains: [
+ 'avatars.githubusercontent.com',
+ 'github.com',
+ 'user-images.githubusercontent.com',
+ 'raw.githubusercontent.com',
+ ],
},
experimental: {
mdxRs: true,
diff --git a/apps/docs/package.json b/apps/docs/package.json
index 4c40db4e3d1..2abc71adb53 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -8,7 +8,7 @@
"start": "next start",
"lint": "next lint",
"build:sitemap": "node ./internals/generate-sitemap.mjs",
- "postbuild": "node ./scripts/build-search.js && node ./internals/generate-sitemap.mjs",
+ "postbuild": "ts-node ./scripts/build-search.ts && node ./internals/generate-sitemap.mjs",
"generate:all": "npm-run-all --parallel gen:api gen:cli gen:gotrue gen:storage gen:supabase-dart:v0 gen:supabase-dart:v1 gen:supabase-js:v1 gen:supabase-js:v2 gen:realtime",
"gen:api": "npm-run-all gen:api:usage",
"gen:api:usage": "ts-node ./generator/index.ts gen --type api --url https://api.supabase.com --input ../../spec/transforms/api_v0_openapi_deparsed.json --output ./docs/reference/api/generated/usage.mdx",
diff --git a/apps/docs/pages/[...slug].tsx b/apps/docs/pages/[...slug].tsx
deleted file mode 100644
index 6b34513a155..00000000000
--- a/apps/docs/pages/[...slug].tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import toc from 'markdown-toc'
-import { MDXRemote } from 'next-mdx-remote'
-import { serialize } from 'next-mdx-remote/serialize'
-import components from '../components/index'
-import NewLayout from '~/layouts/DefaultGuideLayout'
-
-import Layout from '~/layouts/Default'
-import { getAllDocs, getDocsBySlug } from '../lib/docs'
-
-interface Meta {
- id: string
- title: string
- sidebar_label: string
- hide_table_of_contents: boolean
-}
-
-interface Props {
- meta: Meta
- content: any
- toc: any
-}
-const isNewDocs = process.env.NEXT_PUBLIC_NEW_DOCS === 'true'
-
-export default function Doc({ meta, content, toc }: Props) {
- return (
- // @ts-ignore
- <>
- {Object.entries(meta).length > 0 || !isNewDocs ? (
-
-
-
- ) : (
-
-
-
- )}
- >
- )
-}
-
-export async function getStaticProps({ params }: { params: { slug: string[] } }) {
- let slug
- if (params.slug.length > 1) {
- slug = `docs/${params.slug.join('/')}`
- } else {
- slug = `docs/${params.slug[0]}`
- }
-
- let doc = getDocsBySlug(slug)
- const content = await serialize(doc.content || '')
-
- return {
- props: {
- ...doc,
- content,
- toc: toc(doc.content, { maxdepth: 1, firsth1: false }),
- },
- }
-}
-
-export function getStaticPaths() {
- let docs = getAllDocs()
-
- return {
- paths: docs.map(() => {
- return {
- params: {
- slug: docs.map((d) => d.slug),
- },
- }
- }),
- fallback: 'blocking',
- }
-}
diff --git a/apps/docs/pages/faq.mdx b/apps/docs/pages/faq.mdx
index eced1e89829..66c0c120969 100644
--- a/apps/docs/pages/faq.mdx
+++ b/apps/docs/pages/faq.mdx
@@ -16,7 +16,7 @@ Self-hosting Supabase is free. If you wish to use our cloud-platform, we provide
### How do I host Supabase?
-You can use the docker-compose script [here](https://github.com/supabase/supabase/tree/master/docker), and find detailed instructions [here](/docs/guides/hosting/overview).
+You can use the docker compose script [here](https://github.com/supabase/supabase/tree/master/docker), and find detailed instructions [here](/docs/guides/hosting/overview).
Supabase is an amalgamation of open source tools. Some of these tools are made by Supabase (like our [Realtime Server](https://github.com/supabase/realtime)), some we support directly (like [PostgREST](http://postgrest.org/en/v7.0.0/)), and some are third-party tools (like [KonSupabase is an amalgamation open sourceg](https://github.com/Kong/kong)).
diff --git a/apps/docs/pages/guides/auth/auth-email-templates.mdx b/apps/docs/pages/guides/auth/auth-email-templates.mdx
new file mode 100644
index 00000000000..a82c244000b
--- /dev/null
+++ b/apps/docs/pages/guides/auth/auth-email-templates.mdx
@@ -0,0 +1,50 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ title: 'Email Templates',
+ description: 'Learn how to configure the email templates on Supabase.',
+}
+
+You can customize the email messages used for the authentication flows. You can edit the following email templates:
+
+- Confirm signup
+- Invite user
+- Magic Link
+- Change Email Address
+- Reset Password
+
+## Terminology
+
+The templating system provides the following variables for use:
+
+| Name | Description |
+| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `{{ .ConfirmationURL }}` | Contains the confirmation URL. For example, a signup confirmation URL would look like: `https://project-ref.supabase.co/auth/v1/verify?token={{ .TokenHash }}&type=signup&redirect_to=https://example.com/path` . |
+| `{{ .Token }}` | Contains a 6-digit One-Time-Password (OTP) that can be used instead of the `{{. ConfirmationURL }}` . |
+| `{{ .TokenHash }}` | Contains a hashed version of the `{{ .Token }}`. This is useful for constructing your own email link in the email template. |
+| `{{ .SiteURL }}` | Contains your application's Site URL. This can be configured in your project's [authentication settings](https://app.supabase.com/project/_/auth/url-configuration). |
+
+## Limitations
+
+### Email Prefetching
+
+Certain email providers may have spam detection or other security features that prefetch URL links from incoming emails.
+In this scenario, the `{{ .ConfirmationURL }}` sent will be consumed instantly which leads to a "Token has expired or is invalid" error.
+To guard against this:
+
+- Use an email OTP instead by including `{{ .Token }}` in the email template.
+- Create your own custom email link to redirect the user to a page where they can click on a button to confirm the action.
+ For example, you can include the following in your email template:
+
+ ```html
+ Confirm your signup
+
+ ```
+
+ The user should be brought to a page on your site where they can confirm the action by clicking a button.
+ The button should contain the actual confirmation link which can be obtained from parsing the `confirmation_url={{ .ConfirmationURL }}` query parameter in the URL.
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/auth/auth-helpers/nextjs-server-components.mdx b/apps/docs/pages/guides/auth/auth-helpers/nextjs-server-components.mdx
index 346c8d7f3bc..10fa7f73f31 100644
--- a/apps/docs/pages/guides/auth/auth-helpers/nextjs-server-components.mdx
+++ b/apps/docs/pages/guides/auth/auth-helpers/nextjs-server-components.mdx
@@ -95,7 +95,7 @@ Create a new file at `/utils/supabase-browser.ts` and populate with the followin
```ts
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { Database } from '../db_types'
+import { Database } from '../lib/database.types'
export default createBrowserSupabaseClient()
```
@@ -109,7 +109,7 @@ Create a new file at `/utils/supabase-server.ts` and populate with the following
```ts title="/utils/supabase-server.ts"
import { headers, cookies } from 'next/headers'
import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { Database } from '../db_types'
+import { Database } from '../lib/database.types'
export default () =>
createServerComponentSupabaseClient({
@@ -139,6 +139,8 @@ Middleware runs before every route declared in the `matcher` array. Since we don
>
+Create a new file at `/pages/middleware.js` and populate with the following:
+
```jsx title="middleware.js"
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
@@ -164,6 +166,8 @@ export const config = {
+Create a new file at `/middleware.ts` (a sibling of /pages) and populate with the following:
+
```tsx title="middleware.ts"
import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
@@ -254,7 +258,7 @@ export default async function RootLayout({ children }) {
}
```
-We don't want Next.js to cache this `session` value, so we need to export a `revalidate` value of `0`.
+We don't want Next.js to cache this `session` value, so we need to export a `revalidate` value of `0` in the layout file.
```jsx
export const revalidate = 0
@@ -641,7 +645,7 @@ Create a new file at `/app/realtime/posts.tsx` and populate with the following:
'use client'
import { useEffect, useState } from 'react'
-import { Database } from '../../db_types'
+import { Database } from '../lib/database.types'
import supabase from '../../utils/supabase-browser'
type Post = Database['public']['Tables']['posts']['Row']
diff --git a/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx b/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
index c38f8f3b284..76e169c2271 100644
--- a/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
+++ b/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
@@ -83,9 +83,9 @@ Wrap your `pages/_app.js` component with the `SessionContextProvider` component:
```jsx title=pages/_app.js
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider } from '@supabase/auth-helpers-react'
+import { useState } from 'react'
function MyApp({ Component, pageProps }) {
- const router = useRouter()
// Create a new supabase browser client on every first render.
const [supabaseClient] = useState(() => createBrowserSupabaseClient())
@@ -108,6 +108,7 @@ Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component
```tsx lines=2,8 title=pages/_app.tsx
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider, Session } from '@supabase/auth-helpers-react'
+import { useState } from 'react'
function MyApp({
Component,
@@ -162,6 +163,7 @@ const supabaseClient = useSupabaseClient()
```tsx
// Creating a new supabase server client object (e.g. in API route):
+import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
import type { NextApiRequest, NextApiResponse } from 'next'
import type { Database } from 'types_db'
diff --git a/apps/docs/pages/guides/auth/managing-user-data.mdx b/apps/docs/pages/guides/auth/managing-user-data.mdx
index da30e0bb329..5ec883528ff 100644
--- a/apps/docs/pages/guides/auth/managing-user-data.mdx
+++ b/apps/docs/pages/guides/auth/managing-user-data.mdx
@@ -18,7 +18,7 @@ For example, a `public.profiles` table might look like this:
```sql
create table public.profiles (
- id uuid references auth.users not null,
+ id uuid references auth.users not null on delete cascade,
first_name text,
last_name text,
@@ -28,6 +28,12 @@ create table public.profiles (
alter table public.profiles enable row level security;
```
+
+
+Make sure to specify the `on delete cascade` clause when referencing `auth.users`. Omitting it may cause problems when deleting users.
+
+
+
## Public access
Since Row Level Security is enabled, this table is accessible via the API but no data will be returned unless we set up some Policies.
diff --git a/apps/docs/pages/guides/database/extensions/pgnet.mdx b/apps/docs/pages/guides/database/extensions/pgnet.mdx
index f9b99c7e018..29283ab4430 100644
--- a/apps/docs/pages/guides/database/extensions/pgnet.mdx
+++ b/apps/docs/pages/guides/database/extensions/pgnet.mdx
@@ -36,11 +36,11 @@ It differs from the `http` extension in that it is asynchronous by default. This
```sql
--- Example: enable the "pg_net" extension
-create schema if not exists net;
-create extension pg_net with schema net;
+-- Example: enable the "pg_net" extension.
+create extension pg_net;
+-- Note: The extension creates its own schema/namespace named "net" to avoid naming conflicts.
--- Example: disable the "plv8" extension
+-- Example: disable the "pg_net" extension
drop extension if exists pg_net;
drop schema net;
```
diff --git a/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx b/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx
index 2731ae221a2..167e9b92807 100644
--- a/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx
+++ b/apps/docs/pages/guides/database/extensions/uuid-ossp.mdx
@@ -10,8 +10,8 @@ The `uuid-ossp` extension can be used to generate a `UUID`.
## Overview
-A `UUID` is a "Universally Unique Identifer" and it is, for practical purposes, unique.
-This makes them particularly well suited as Primary Keys. It is occasionally referred to as a `GUID`, which stands for "Globally Unique Identifer".
+A `UUID` is a "Universally Unique Identifier" and it is, for practical purposes, unique.
+This makes them particularly well suited as Primary Keys. It is occasionally referred to as a `GUID`, which stands for "Globally Unique Identifier".
## Usage
diff --git a/apps/docs/pages/guides/database/tables.mdx b/apps/docs/pages/guides/database/tables.mdx
index 010b104a2a4..78d1b62115a 100644
--- a/apps/docs/pages/guides/database/tables.mdx
+++ b/apps/docs/pages/guides/database/tables.mdx
@@ -265,7 +265,7 @@ For example if you had the following situations:
- You have a list of `movies`.
- A movie can have several `actors`.
-- An `actor` can perfom in several movies.
+- An `actor` can perform in several movies.
+
{webapps.map((item) => {
return (
@@ -150,13 +150,13 @@ export const resources = [
{
title: 'Features',
hasLightIcon: true,
- href: '/features',
+ href: '/guides/getting-started/features',
description: 'A non-exhaustive list of features that Supabase provides for every project.',
},
{
title: 'Architecture',
hasLightIcon: true,
- href: '/architecture',
+ href: '/guides/getting-started/architecture',
description: "An overview of Supabase's architecture and product principles.",
},
]
diff --git a/apps/docs/pages/guides/getting-started/features.mdx b/apps/docs/pages/guides/getting-started/features.mdx
index 4b10f3830ba..c1e1a0dcfc9 100755
--- a/apps/docs/pages/guides/getting-started/features.mdx
+++ b/apps/docs/pages/guides/getting-started/features.mdx
@@ -60,11 +60,11 @@ Build passwordless logins for your application or website.[Docs](/docs/guides/au
### Social Logins
-Provide social logins - everything from Apple, to GitHub, to Slack. [Docs](/docs/guides/auth/auth-apple).
+Provide social logins - everything from Apple, to GitHub, to Slack. [Docs](/docs/guides/auth/social-login).
### Phone Logins
-Provide phone logins using a 3rd-party SMS provider. [Docs](/docs/guides/auth/auth-twilio).
+Provide phone logins using a third-party SMS provider. [Docs](/docs/guides/auth/phone-login).
### Row Level Security
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx
index b0d5fc21c93..8f2754453f9 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-angular.mdx
@@ -205,7 +205,7 @@ export class AuthComponent implements OnInit {
-
```
### Account page
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx
index db3e028ad30..6a73c7aa8ab 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-flutter.mdx
@@ -50,7 +50,9 @@ Add `io.supabase.flutterquickstart://login-callback/` as a new [redirect URL](ht
That is it on Supabase's end and the rest are platform specific settings:
-For Android, add an intent-filter to enable deep linking:
+For Android, edit the `android/app/src/main/AndroidManifest.xml` file.
+
+Add an intent-filter to enable deep linking:
```xml title=android/app/src/main/AndroidManifest.xml
@@ -75,7 +77,9 @@ For Android, add an intent-filter to enable deep linking:
```
-For iOS add CFBundleURLTypes to enable deep linking:
+For iOS, edit the ios/Runner/Info.plist file.
+
+Add CFBundleURLTypes to enable deep linking:
```xml title=ios/Runner/Info.plist"
diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx
index 1f7804f15ab..7d1418b7146 100644
--- a/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx
+++ b/apps/docs/pages/guides/getting-started/tutorials/with-react.mdx
@@ -37,7 +37,7 @@ Then let's install the only additional dependency: [supabase-js](https://github.
npm install @supabase/supabase-js
```
-And finally we want to save the environment variables in a `.env`.
+And finally we want to save the environment variables in a `.env.local` file.
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
```bash title=.env
@@ -48,6 +48,8 @@ REACT_APP_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
Now that we have the API credentials in place, let's create a helper file to initialize the Supabase client. These variables will be exposed
on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
+Create and edit `src/supabaseClient.js`:
+
```js title=src/supabaseClient.js
import { createClient } from '@supabase/supabase-js'
@@ -64,6 +66,8 @@ You can find the full contents of this file [here](https://raw.githubusercontent
Let's set up a React component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
+Create and edit `src/Auth.js`:
+
```jsx title=src/Auth.js
import { useState } from 'react'
import { supabase } from './supabaseClient'
@@ -120,7 +124,7 @@ export default function Auth() {
After a user is signed in we can allow them to edit their profile details and manage their account.
-Let's create a new component for that called `Account.js`.
+Let's create a new component for that called `src/Account.js`.
```jsx title=src/Account.js
import { useState, useEffect } from 'react'
@@ -234,7 +238,7 @@ export default Account
### Launch!
-Now that we have all the components in place, let's update `App.js`:
+Now that we have all the components in place, let's update `src/App.js`:
```jsx title=src/App.js
import './index.css'
@@ -282,6 +286,8 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component:
+Create and edit `src/Avatar.js`:
+
```jsx title=src/Avatar.js
import { useEffect, useState } from 'react'
import { supabase } from './supabaseClient'
@@ -367,7 +373,7 @@ export default function Avatar({ url, size, onUpload }) {
### Add the new widget
-And then we can add the widget to the Account page:
+And then we can add the widget to the Account page at `src/Account.js`:
```jsx title=src/Account.js
// Import the new component
@@ -376,7 +382,7 @@ import Avatar from './Avatar'
// ...
return (
-
+
- );
+ )
}
```
Now we will use Supabase's `withPageAuth` wrapper around `getServerSideProps` to make the home page require authentication via SSR. Replace the existing code in `/pages/index.tsx` with the following:
-```
-import { getUser, User, withPageAuth } from "@supabase/auth-helpers-nextjs";
-import { GetServerSideProps } from "next";
-import { useRouter } from "next/router";
-import { authsignal } from "../lib/authsignal";
+```ts
+import { getUser, User, withPageAuth } from '@supabase/auth-helpers-nextjs'
+import { GetServerSideProps } from 'next'
+import { useRouter } from 'next/router'
+import { authsignal } from '../lib/authsignal'
interface Props {
- user: User;
- isEnrolled: boolean;
+ user: User
+ isEnrolled: boolean
}
export const getServerSideProps: GetServerSideProps = withPageAuth({
- redirectTo: "/sign-in",
+ redirectTo: '/sign-in',
async getServerSideProps(ctx) {
- const { user } = await getUser(ctx);
+ const { user } = await getUser(ctx)
- const { isEnrolled } = await authsignal.getUser({userId: user.id});
+ const { isEnrolled } = await authsignal.getUser({ userId: user.id })
return {
props: { user, isEnrolled },
- };
+ }
},
-});
+})
export default function HomePage({ user, isEnrolled }: Props) {
- const router = useRouter();
+ const router = useRouter()
return (
@@ -351,29 +340,29 @@ export default function HomePage({ user, isEnrolled }: Props) {
Signed in as: {user?.email}
-
+
- );
+ )
}
```
Optional: To make things look a bit nicer, you can add the following to `/styles/globals.css`:
-```
+```css
main {
min-height: 100vh;
display: flex;
@@ -435,113 +424,95 @@ Now we'll replace the existing api routes in `/pages/api/` with 5 new routes:
Add the following code to `/pages/api/sign-in.ts`:
-```
-import { supabaseClient } from "@supabase/auth-helpers-nextjs";
-import { NextApiRequest, NextApiResponse } from "next";
-import { authsignal } from "../../lib/authsignal";
-import { setAuthCookie, setTempCookie } from "../../lib/cookies";
+```ts
+import { supabaseClient } from '@supabase/auth-helpers-nextjs'
+import { NextApiRequest, NextApiResponse } from 'next'
+import { authsignal } from '../../lib/authsignal'
+import { setAuthCookie, setTempCookie } from '../../lib/cookies'
-export default async function signIn(
- req: NextApiRequest,
- res: NextApiResponse
-) {
- const { email, password } = req.body;
+export default async function signIn(req: NextApiRequest, res: NextApiResponse) {
+ const { email, password } = req.body
- const { data, error } = await supabaseClient.auth.api.signInWithEmail(
- email,
- password
- );
+ const { data, error } = await supabaseClient.auth.api.signInWithEmail(email, password)
if (error || !data?.user) {
- return res.send({ error });
+ return res.send({ error })
}
const { state, url: mfaUrl } = await authsignal.track({
- action: "signIn",
+ action: 'signIn',
userId: data.user.id,
- });
+ })
- if (state === "CHALLENGE_REQUIRED") {
- await setTempCookie(data, res);
+ if (state === 'CHALLENGE_REQUIRED') {
+ await setTempCookie(data, res)
} else {
- setAuthCookie(data, res);
+ setAuthCookie(data, res)
}
- res.send({ state, mfaUrl });
+ res.send({ state, mfaUrl })
}
```
Then to handle new sign-ups add the following to `/pages/api/sign-up.ts`:
-```
-import { supabaseClient } from "@supabase/auth-helpers-nextjs";
-import { Session } from "@supabase/supabase-js";
-import { NextApiRequest, NextApiResponse } from "next";
-import { setAuthCookie } from "../../lib/cookies";
+```ts
+import { supabaseClient } from '@supabase/auth-helpers-nextjs'
+import { Session } from '@supabase/supabase-js'
+import { NextApiRequest, NextApiResponse } from 'next'
+import { setAuthCookie } from '../../lib/cookies'
-export default async function signUp(
- req: NextApiRequest,
- res: NextApiResponse
-) {
- const { email, password } = req.body;
+export default async function signUp(req: NextApiRequest, res: NextApiResponse) {
+ const { email, password } = req.body
- const { data, error } = await supabaseClient.auth.api.signUpWithEmail(
- email,
- password
- );
+ const { data, error } = await supabaseClient.auth.api.signUpWithEmail(email, password)
if (error || !isSession(data)) {
- res.send({ error });
+ res.send({ error })
} else {
- setAuthCookie(data, res);
- res.send({ data });
+ setAuthCookie(data, res)
+ res.send({ data })
}
}
-const isSession = (data: any): data is Session => !!data?.access_token;
+const isSession = (data: any): data is Session => !!data?.access_token
```
To clear the auth cookies on sign-out add the following to `/pages/api/sign-out.ts`:
-```
-import { supabaseClient } from "@supabase/auth-helpers-nextjs";
-import { NextApiRequest, NextApiResponse } from "next";
+```ts
+import { supabaseClient } from '@supabase/auth-helpers-nextjs'
+import { NextApiRequest, NextApiResponse } from 'next'
-export default async function signOut(
- req: NextApiRequest,
- res: NextApiResponse
-) {
- supabaseClient.auth.api.deleteAuthCookie(req, res, { redirectTo: "/sign-in" });
+export default async function signOut(req: NextApiRequest, res: NextApiResponse) {
+ supabaseClient.auth.api.deleteAuthCookie(req, res, { redirectTo: '/sign-in' })
}
```
To handle the user's actions to set up MFA or manage their existing MFA settings, add the following to `/pages/api/mfa.ts`:
-```
-import { getUser, withApiAuth } from "@supabase/auth-helpers-nextjs";
-import { NextApiRequest, NextApiResponse } from "next";
-import { authsignal } from "../../lib/authsignal";
+```ts
+import { getUser, withApiAuth } from '@supabase/auth-helpers-nextjs'
+import { NextApiRequest, NextApiResponse } from 'next'
+import { authsignal } from '../../lib/authsignal'
-export default withApiAuth(async function mfa(
- req: NextApiRequest,
- res: NextApiResponse
-) {
- if (req.method !== "POST") {
- return res.status(405).send({ message: "Only POST requests allowed" });
+export default withApiAuth(async function mfa(req: NextApiRequest, res: NextApiResponse) {
+ if (req.method !== 'POST') {
+ return res.status(405).send({ message: 'Only POST requests allowed' })
}
- const { user } = await getUser({ req, res });
+ const { user } = await getUser({ req, res })
- const { isEnrolled } = req.body;
+ const { isEnrolled } = req.body
const { url: mfaUrl } = await authsignal.track({
- action: isEnrolled ? "manageSettings" : "enroll",
+ action: isEnrolled ? 'manageSettings' : 'enroll',
userId: user.id,
redirectToSettings: isEnrolled,
- });
+ })
- res.send({ mfaUrl });
-});
+ res.send({ mfaUrl })
+})
```
Because the user should be authenticated with Supabase to set up or manage MFA, we can use Supabase's `withApiAuth` wrapper to protect this route.
@@ -550,28 +521,25 @@ The `redirectToSettings` param specifies whether the user should be redirected t
Finally we need a route to handle the redirect back from Authsignal after an MFA challenge. Add the following to `/pages/api/callback.ts`:
-```
-import { NextApiRequest, NextApiResponse } from "next";
-import { authsignal } from "../../lib/authsignal";
-import { getSessionFromTempCookie, setAuthCookie } from "../../lib/cookies";
+```ts
+import { NextApiRequest, NextApiResponse } from 'next'
+import { authsignal } from '../../lib/authsignal'
+import { getSessionFromTempCookie, setAuthCookie } from '../../lib/cookies'
-export default async function callback(
- req: NextApiRequest,
- res: NextApiResponse
-) {
- const token = req.query.token as string;
+export default async function callback(req: NextApiRequest, res: NextApiResponse) {
+ const token = req.query.token as string
- const { success } = await authsignal.validateChallenge({ token });
+ const { success } = await authsignal.validateChallenge({ token })
if (success) {
- const session = await getSessionFromTempCookie(req);
+ const session = await getSessionFromTempCookie(req)
if (session) {
- setAuthCookie(session, res);
+ setAuthCookie(session, res)
}
}
- res.redirect("/");
+ res.redirect('/')
}
```
diff --git a/apps/docs/pages/guides/integrations/onesignal.mdx b/apps/docs/pages/guides/integrations/onesignal.mdx
new file mode 100644
index 00000000000..cd6b7021dd8
--- /dev/null
+++ b/apps/docs/pages/guides/integrations/onesignal.mdx
@@ -0,0 +1,341 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'onesignal',
+ title: 'OneSignal',
+ description:
+ 'OneSignal allows you to send cloud messages to your users. Combine OneSignal with your Supabase apps and you can reach out to your users whenever there is a change in your database.',
+}
+
+[OneSignal](https://onesignal.com/) is a tool that allows you to send messages across different channels such as the following to keep your users engaged.
+
+- Push notifications
+- SMS
+- Emails
+- In-app notifications
+
+This guide steps you through how you can integrate OneSignal with Supabase to create a seamless cloud messaging experience for your users using Database webhooks and edge functions through a simple Next.js application.
+
+
+
+We will create a simple ordering app and use Supabase Database Webhooks in conjunction with Edge Function to provide a real-time push notification experience.
+
+You can find the complete example app along with the edge functions code to send the notifications [here](https://github.com/supabase-community/onesignal).
+
+
+
+
+## Step 1: Getting started
+
+Before we dive into the code, this guide assumes that you have the following ready
+
+- [Supabase](https://supabase.com/) project created
+- [OneSignal](https://onesignal.com/) app created
+- [Supabase CLI](https://supabase.com/docs/guides/cli) installed on your machine
+
+Let’s create a Next.js app with tailwind CSS pre-installed
+
+```bash
+npx create-next-app -e with-tailwindcss --ts
+```
+
+We will then install the Supabase and OneSignal SDK.
+
+```bash
+npm i @supabase/supabase-js
+npm i react-onesignal
+```
+
+After that, follow the instructions [here](https://documentation.onesignal.com/docs/web-push-custom-code-setup) to set up OneSignal for the web. You can set the URL of the app as a local host if you want to run the app locally, or add a remote URL if you want to deploy your app to a public hosting. You should add the file you obtain in step 4 of the instruction under the `public` directory of your Next.js app like [this](https://github.com/supabase-community/onesignal/tree/main/app/public).
+
+## Step 2: Build Next.js app
+
+The Next.js app will have a login form for the user to sign in, and a button that they can press to make an order once they are signed in. Update the `index.tsx` file to the following.
+
+```tsx title="pages/index.tsx"
+import { createClient, User } from '@supabase/supabase-js'
+import type { NextPage } from 'next'
+import Head from 'next/head'
+import React, { useEffect, useState } from 'react'
+import OneSignal from 'react-onesignal'
+
+const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
+const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
+const oneSignalAppId = process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID!
+
+const supabase = createClient(supabaseUrl, supabaseAnonKey)
+
+const Home: NextPage = () => {
+ const [user, setUser] = useState(null)
+
+ const [oneSignalInitialized, setOneSignalInitialized] =
+ useState(false)
+
+ /**
+ * Initializes OneSignal SDK for a given Supabase User ID
+ * @param uid Supabase User ID
+ */
+ const initializeOneSignal = async (uid: string) => {
+ if (oneSignalInitialized) {
+ return
+ }
+ setOneSignalInitialized(true)
+ await OneSignal.init({
+ appId: oneSignalAppId,
+ notifyButton: {
+ enable: true,
+ },
+
+ allowLocalhostAsSecureOrigin: true,
+ })
+
+ await OneSignal.setExternalUserId(uid)
+ }
+
+ const sendMagicLink = async (event: React.FormEvent) => {
+ event.preventDefault()
+ const { email } = Object.fromEntries(new FormData(event.currentTarget))
+ if (typeof email !== 'string') return
+
+ const { error } = await supabase.auth.signInWithOtp({ email })
+ if (error) {
+ alert(error.message)
+ } else {
+ alert('Check your email inbox')
+ }
+ }
+
+ // Place a order with the selected price
+ const submitOrder = async (event: React.FormEvent) => {
+ event.preventDefault()
+ const { price } = Object.fromEntries(new FormData(event.currentTarget))
+ if (typeof price !== 'string') return
+
+ const { error } = await supabase
+ .from('orders')
+ .insert({ price: Number(price) })
+ if (error) {
+ alert(error.message)
+ }
+ }
+
+ useEffect(() => {
+ const initialize = async () => {
+ const initialUser = (await supabase.auth.getUser())?.data.user
+ setUser(initialUser ?? null)
+ if (initialUser) {
+ initializeOneSignal(initialUser.id)
+ }
+ }
+
+ initialize()
+
+ const authListener = supabase.auth.onAuthStateChange(
+ async (event, session) => {
+ const user = session?.user ?? null
+ setUser(user)
+ if (user) {
+ initializeOneSignal(user.id)
+ }
+ }
+ )
+
+ return () => {
+ authListener.data.subscription.unsubscribe()
+ }
+ }, [])
+
+ return (
+ <>
+
+ OneSignal Order Notification App
+
+
+
+
+ {user ? (
+
+
+
+
+ ) : (
+
+
+
+
+ )}
+
+ >
+ )
+}
+
+export default Home
+```
+
+There is quite a bit of stuff going on here, but basically, it’s creating a simple UI for the user to sign in using the [magic link](https://supabase.com/docs/guides/auth/auth-magic-link), and once the user is signed in, will initialize OneSignal to ask the user to receive notifications on the website.
+
+Notice that inside the `initializeOneSignal()` function, we are setting the Supabase user ID as an [external user ID of OneSignal](https://documentation.onesignal.com/docs/external-user-ids). This allows us to later send push notifications to the user using their Supabase user ID from the backend, which is very handy.
+
+```tsx
+await OneSignal.setExternalUserId(uid);
+```
+
+The front-end side of things is done here. Let’s get into the backend.
+
+We also need to set our environment variables. Create a `.env.local` file and use the following template to set the environment variables. You can find your Supabase configuration in your dashboard under `settings > API`, and you can find the OneSignal app ID from `Settings > Keys & IDs`
+
+```bash
+NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
+NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
+NEXT_PUBLIC_ONESIGNAL_APP_ID=YOUR_ONESIGNAL_APP_ID
+```
+
+
+
+## Step 3: Create the Edge Function
+
+Let’s create an edge function that will receive [database webhooks](https://supabase.com/docs/guides/database/webhooks) from the database and calls the OneSignal API to send the push notification.
+
+```bash
+supabase functions new notify
+```
+
+Replace the contents of `supabase/functions/notify/index.ts` with the following
+
+```tsx
+import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
+import * as OneSignal from 'https://esm.sh/@onesignal/node-onesignal@1.0.0-beta7'
+
+const _OnesignalAppId_ = Deno.env.get('ONESIGNAL_APP_ID')!
+const _OnesignalUserAuthKey_ = Deno.env.get('USER_AUTH_KEY')!
+const _OnesignalRestApiKey_ = Deno.env.get('ONESIGNAL_REST_API_KEY')!
+const configuration = OneSignal.createConfiguration({
+ userKey: _OnesignalUserAuthKey_,
+ appKey: _OnesignalRestApiKey_,
+})
+
+const onesignal = new OneSignal.DefaultApi(configuration)
+
+serve(async (req) => {
+ try {
+ const { record } = await req.json()
+
+ // Build OneSignal notification object
+ const notification = new OneSignal.Notification()
+ notification.app_id = _OnesignalAppId_
+ notification.include_external_user_ids = [record.user_id]
+ notification.contents = {
+ en: `You just spent $${record.price}!`,
+ }
+ const onesignalApiRes = await onesignal.createNotification(notification)
+
+ return new Response(
+ JSON.stringify({ onesignalResponse: onesignalApiRes }),
+ {
+ headers: { 'Content-Type': 'application/json' },
+ }
+ )
+ } catch (err) {
+ console.error('Failed to create OneSignal notification', err)
+ return new Response('Server error.', {
+ headers: { 'Content-Type': 'application/json' },
+ status: 400,
+ })
+ }
+})
+```
+
+If you see bunch of errors in your editor, it's because your editor is not configured to use Deno. Follow the official setup guide [here](https://deno.land/manual@v1.28.3/getting_started/setup_your_environment) to setup your IDE to use Deno.
+
+The function receives a `record` object, which is the row inserted in your `orders` table, and constructs a notification object to then send to OneSignal to deliver the push notification.
+
+We also need to set the environment variable for the function. Create a `.env` file under your `supabase` directory and paste the following.
+
+```bash
+ONESIGNAL_APP_ID=YOUR_ONESIGNAL_APP_ID
+USER_AUTH_KEY=YOUR_USER_AUTH_KEY
+ONESIGNAL_REST_API_KEY=YOUR_ONESIGNAL_REST_API_KEY
+```
+
+`ONESIGNAL_APP_ID` and `ONESIGNAL_REST_API_KEY` can be found under `Settings > Keys & IDs` of your OneSignal app, and `USER_AUTH_KEY` can be found by going to `Account & API Keys` page by clicking your icon in the top right corner and scrolling to the `User Auth Key` section.
+
+
+
+
+Once your environment variables are filled in, you can run the following command to set the environment variable.
+
+```bash
+supabase secrets set --env-file ./supabase/.env
+```
+
+At this point, the function should be ready to be deployed! Run the following command to deploy your functions to the edge! The `no-verify-jwt` flag is required if you plan to call the function from a webhook.
+
+```bash
+supabase functions deploy notify --no-verify-jwt
+```
+
+## Step 4: Setting up the Supabase database
+
+Finally, we get to set up the database! Run the following SQL to set up the `orders` table.
+
+```sql
+create table if not exists public.orders (
+ id uuid not null primary key default uuid_generate_v4(),
+ created_at timestamptz not null default now(),
+ user_id uuid not null default auth.uid(),
+ price int8 not null
+);
+```
+
+As you can see, the `orders` table has 4 columns and 3 of them have default values. That means all we need to send from the front-end app is the price. That is why our insert statement looked very simple.
+
+```tsx
+const { error } = await supabase.from('orders').insert({
+ price: 100,
+ })
+```
+
+Let’s also set up the webhook so that whenever a new row is inserted in the `orders` table, it calls the edge function. Go to `Database > Webhooks` and create a new Database Webhook. The table should be set to `orders` and Events should be inserted. The type should be HTTP Request, the HTTP method should be POST, and the URL should be the URL of your edge function. Hit confirm to save the webhook configuration.
+
+
+
+At this point, the app should be complete! Run your app locally with `npm run dev`, or deploy your app to a hosting service and see how you receive a push notification when you place an order!
+Remember that if you decide to deploy your app to a hosting service, you would need to create another OneSignal app configured for your local address.
+
+
+
+
+## Resources
+
+This particular example was using Next.js, but you can apply the same principles to implement send push notification, SMS, Emails, and in-app-notifications on other platforms as well.
+
+- [OneSignal + Flutter + Supabase example](https://github.com/OneSignalDevelopers/onesignal-supabase-sample-integration-supabase)
+- [OneSignal Mobile Quickstart](https://documentation.onesignal.com/docs/mobile-sdk-setup)
+- [OneSignal Documentation](https://documentation.onesignal.com/docs/onesignal-platform)
+- [OneSignal Onboarding guide](https://documentation.onesignal.com/docs/onboarding-with-onesignal)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/integrations/weweb.mdx b/apps/docs/pages/guides/integrations/weweb.mdx
index 93ddbd5f2bc..c65a695400e 100644
--- a/apps/docs/pages/guides/integrations/weweb.mdx
+++ b/apps/docs/pages/guides/integrations/weweb.mdx
@@ -39,12 +39,7 @@ Once you have added both to WeWeb, you will have the option to enable realtime t
**🚨 Warning 🚨**
-> Realtime is disabled by default in Supabase for better database performance and security. Learn more about [realtime functionalities](https://supabase.com/docs/guides/database#realtime) in our documentation.
-> Member
-> @awalias awalias 16 days ago
-> might just wanna change these ones to be relative but otherwise looks great 👍
-
-@EWMAs Reply...
+> Realtime is disabled by default in Supabase for better database performance and security. Learn more about [realtime functionalities](/docs/guides/realtime).
## Step 2: GET Data from Supabase
diff --git a/apps/docs/pages/guides/platform/access-control.mdx b/apps/docs/pages/guides/platform/access-control.mdx
index f470d14af9a..b12d566cc0c 100644
--- a/apps/docs/pages/guides/platform/access-control.mdx
+++ b/apps/docs/pages/guides/platform/access-control.mdx
@@ -1,84 +1,81 @@
import Layout from '~/layouts/DefaultGuideLayout'
+import { IconCheck } from 'ui'
export const meta = {
title: 'Access Control',
description: 'Roles and permissions at the organization level',
}
-Supabase provides granular access control features that let you manage permissions across your organizations.
-
-Within a Supabase organization, a member can have one of the following roles:
+Supabase provides granular access controls to manage permissions across your organizations.
+For each organization, a member can have one of the following roles:
- Owner
- Administrator
- Developer
-A default organization is created for a user when they first sign-in and
-assigned the **Owner** role. If the user wants to invite others
-to collaborate within the organization, they can visit the organization team
-settings (`https://app.supabase.com/org//settings#team`) to send an
-invite link to another user's email. The invite expires after 24 hours.
-
-Invites sent from a SSO account can only be accepted by another SSO account
-coming from the same identity provider. This is a security measure that
-prevents accidental invites to accounts not managed by your company's
-enterprise systems.
-
-Project level invites are not available at this time. A member of the
-organization will be able to access all projects under the organization. If you
-wish to restrict access to certain projects, please create another organization
-to manage this.
+A default organization is created when you first sign in and
+you'll be assigned the **Owner** role.
+Each member can access all projects under the organization.
+Project level invites are not available at this time.
+Create a separate organization if you need to restrict access to certain projects.
## Manage team members
-You can invite your team members into your organizations to collaborate on projects.
+To invite others to collaborate, visit your organization's team settings in the
+[Dashboard](https://app.supabase.com/projects) to send an invite link to
+another user's email. The invite expires after 24 hours.
-
+### Transferring ownership of an organization
+
+Each Supabase organization can have one or more owners. If you no longer want be an owner of an organization, click **Leave team** in the members view (`https://app.supabase.com/org//settings#team`) of your organization.
+However, you can only leave an organization when there is _at least one other owner_.
+
+If you are transferring ownership of your organization to someone else, you will need to invite the new member with the **Owner** role. You can leave the organization after they've accepted the invitation.
### Permissions across roles [#permission-across-roles]
The table below shows the corresponding permissions for each available role you can assign a team member in the Dashboard.
-| Permissions | Owner | Administrator | Developer |
-| ------------------------ | ----- | ------------- | --------- |
+| Permissions | Owner | Administrator | Developer |
+| ------------------------ | ----------------------- | ----------------------- | ----------------------- |
| **Organization** |
-| Change organization name | ✅ | | |
-| Delete organization | ✅ | | |
+| Change organization name | | | |
+| Delete organization | | | |
| **Members** |
-| Add an Owner | ✅ | | |
-| Remove an Owner | ✅ | | |
-| Add an Administrator | ✅ | ✅ | |
-| Remove an Administrator | ✅ | ✅ | |
-| Add a Developer | ✅ | ✅ | |
-| Remove a Developer | ✅ | ✅ | |
-| Revoke an invite | ✅ | ✅ | |
-| Resend an invite | ✅ | ✅ | |
-| Accept an invite[^1] | ✅ | ✅ | ✅ |
+| Add an Owner | | | |
+| Remove an Owner | | | |
+| Add an Administrator | | | |
+| Remove an Administrator | | | |
+| Add a Developer | | | |
+| Remove a Developer | | | |
+| Revoke an invite | | | |
+| Resend an invite | | | |
+| Accept an invite[^1] | | | |
| **Billing** |
-| Read invoices | ✅ | ✅ | ✅ |
-| Read billing email | ✅ | ✅ | ✅ |
-| Change billing email | ✅ | | |
-| View subscription | ✅ | ✅ | ✅ |
-| Update subscription | ✅ | ✅ | |
-| Read billing address | ✅ | ✅ | ✅ |
-| Update billing address | ✅ | ✅ | |
-| Read tax codes | ✅ | ✅ | ✅ |
-| Update tax codes | ✅ | ✅ | |
-| Read payment methods | ✅ | ✅ | ✅ |
-| Update payment methods | ✅ | ✅ | |
+| Read invoices | | | |
+| Read billing email | | | |
+| Change billing email | | | |
+| View subscription | | | |
+| Update subscription | | | |
+| Read billing address | | | |
+| Update billing address | | | |
+| Read tax codes | | | |
+| Update tax codes | | | |
+| Read payment methods | | | |
+| Update payment methods | | | |
| **Projects** |
-| Create a project | ✅ | ✅ | |
-| Delete a project | ✅ | ✅ | |
-| Update a project | ✅ | ✅ | |
-| Pause a project | ✅ | ✅ | |
-| Resume a project | ✅ | ✅ | |
-| Restart a project | ✅ | ✅ | ✅ |
+| Create a project | | | |
+| Delete a project | | | |
+| Update a project | | | |
+| Pause a project | | | |
+| Resume a project | | | |
+| Restart a project | | | |
[^1]:
- If the invite was sent from a SSO account, it can only be accepted from
- a user signed in via the same identity provider.
+ Invites sent from a SSO account can only be accepted by another SSO account
+ coming from the same identity provider. This is a security measure that
+ prevents accidental invites to accounts not managed by your company's
+ enterprise systems.
export const Page = ({ children }) =>
diff --git a/apps/docs/pages/guides/platform/backups.mdx b/apps/docs/pages/guides/platform/backups.mdx
new file mode 100644
index 00000000000..83d606cd856
--- /dev/null
+++ b/apps/docs/pages/guides/platform/backups.mdx
@@ -0,0 +1,77 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ title: 'Database Backups',
+ description: 'Learn about the available backup methods for your Supabase project.',
+}
+
+Database backups are an integral part of any disaster recovery plan. Disasters come in many shapes and sizes. It could be as simple as accidentally deleting a table column, the database crashing, or even a natural calamity wiping out the underlying hardware a database is running on. The risks and impact brought by these scenarios can never be fully eliminated, but only minimized or even mitigated. Having database backups is a form of insurance policy. They are essentially snapshots of the database at various points in time. When disaster strikes, database backups allow the project to be brought back to any of these points in time, therefore averting the crisis.
+
+## Frequency of Backups
+
+When deciding how often a database should be backed up, the key business metric Recovery Point Objective (RPO) should be considered. RPO is the threshold for how much data, measured in time, a business could lose when disaster strikes. This amount is fully dependent on a business and its underlying requirements. A low RPO would mean that database backups would have to be taken at an increased cadence throughout the day. Each Supabase project has access to two forms of backups, Daily Backups and Point-in-Time Recovery. The agreed upon RPO would be a deciding factor in choosing which solution best fits a project.
+
+
+ Database backups do not include objects stored via the Storage API, as the database only includes
+ metadata about these objects. Restoring an old backup does not restore objects that have been
+ deleted since then.
+
+
+## Daily Backups
+
+All Pro and Enterprise tier Supabase projects are backed up automatically on a daily basis. In terms of Recovery Point Objective (RPO), Daily Backups would be suitable for projects willing to lose up to 24 hours worth of data if disaster hits at the most inopportune time. If a lower RPO is required, enabling Point-in-Time Recovery should be considered.
+
+### Backup Process [#daily-backups-process]
+
+The PostgreSQL utility [pg_dumpall](https://www.postgresql.org/docs/current/app-pg-dumpall.html) is used to perform daily backups. An SQL file is generated, zipped up, and sent to our storage servers for safe keeping.
+
+
+
+You can access daily backups in the [Scheduled backups](https://app.supabase.com/project/_/database/backups/scheduled) settings in the Dashboard. Pro tier projects can access the last 7 days’ worth of daily backups while Enterprise tier projects can access up to 30 days’ worth of daily backups. Users can restore their project to any one of the backups or download them as a zipped SQL file.
+
+### Restoration Process [#daily-backups-restoration-process]
+
+When selecting a backup to restore to, select the closest available one made before the desired point in time to restore to. Earlier backups can always be chosen too but do consider the number of days’ worth of data that could be lost.
+
+
+
+The Dashboard will then prompt for a confirmation before proceeding with the restoration. The project will be inaccessible following this. As such, do ensure to allot downtime beforehand. This is dependent on the size of the database. The larger it is, the longer the downtime will be. Once the confirmation has been given, the underlying SQL of the chosen backup is then run against the project. The PostgreSQL utility [psql](https://www.postgresql.org/docs/current/app-psql.html) is used to facilitate the restoration. The Dashboard will display a notification once the restoration completes.
+
+{/* screenshot of the Dashboard of the project completing restoration */}
+
+## Point-in-Time Recovery
+
+Point-in-Time Recovery (PITR) allows a project to be backed up at much shorter intervals. This provides users an option to restore to any chosen point of up to seconds in granularity. Even with daily backups, a day’s worth of data could still be lost. With PITR, backups could be performed up to the point of disaster.
+
+
+ This feature is available to all Enterprise tier projects. Pro tier projects can enable PITR as an
+ add-on.
+
+
+### Backup Process [#pitr-backup-process]
+
+As discussed [here](https://supabase.com/blog/postgresql-physical-logical-backups), PITR is made possible by a combination of taking physical backups of a project, as well as archiving [Write Ahead Log (WAL)](https://www.postgresql.org/docs/current/wal-intro.html) files. Physical backups provide a snapshot of the underlying directory of the database, while WAL files contain records of every change made in the database.
+
+Supabase uses [WAL-G](https://github.com/wal-g/wal-g), an open source archival and restoration tool, to handle both aspects of PITR. On a daily basis, a snapshot of the database is taken and sent to our storage servers. Throughout the day, as database transactions occur, WAL files are generated and uploaded.
+
+By default, WAL files are backed up at two minute intervals. If these files cross a certain file size threshold, they are backed up immediately. As such, during periods of high amount of transactions, WAL file backups become more frequent. Conversely, when there is no activity in the database, WAL file backups are not made. Overall, this would mean that at the worst case scenario or disaster, the PITR achieves a Recovery Point Objective (RPO) of two minutes.
+
+
+
+You can access PITR in the [Point in Time](https://app.supabase.com/project/_/database/backups/pitr) settings in the Dashboard. The recovery period of a project is indicated by the earliest and latest points of recoveries displayed in one’s preferred timezone. If need be, the maximum amount of this recovery period can be modified accordingly.
+
+Note that the latest restore point of the project could be significantly far from the current time. This occurs when there has not been any recent activity in the database, and therefore no WAL file backups have been made recently. This is perfectly fine as the state of the database at the latest point of recovery would still be indicative of the state of the database at the current time given that no transactions have been made in between.
+
+### Restoration Process [#pitr-restoration-process]
+
+
+
+A date and time picker will be provided upon pressing the `Start a restore` button. The process will only proceed if the selected date and time fall within the earliest and latest points of recoveries.
+
+
+
+After locking in the desired point in time to recover to, The Dashboard will then prompt for a review and confirmation before proceeding with the restoration. The project will be inaccessible following this. As such, do ensure to allot for downtime beforehand. This is dependent on the size of the database. The larger it is, the longer the downtime will be. Once the confirmation has been given, the latest physical backup available is downloaded to the project and the database is partially restored. WAL files generated after this physical backup up to the specified point-in-time are then downloaded. The underlying records of transactions in these files are replayed against the database to complete the restoration. The Dashboard will display a notification once the restoration completes.
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/platform/custom-domains.mdx b/apps/docs/pages/guides/platform/custom-domains.mdx
index 2690eb5f23b..13fedb8e525 100644
--- a/apps/docs/pages/guides/platform/custom-domains.mdx
+++ b/apps/docs/pages/guides/platform/custom-domains.mdx
@@ -12,18 +12,20 @@ Custom Domains are currently in beta, and are being slowly made available to pro
-Custom domains allow you to present a branded experience to your users. Currently, you must use a subdomain (e.g., `api.example.com`, rather than `example.com`) for the purposes of this guide.
+Custom domains allow you to present a branded experience to your users. Custom domains are available as a [add-on for projects on a paid tier](https://app.supabase.com/project/_/settings/billing/update). Setting up a custom domain requires [Owner or Admin permissions](/docs/guides/platform/access-control#manage-team-members) for the project. Currently, you must use a subdomain (e.g., `api.example.com`, rather than `example.com`) for the purposes of this guide.
+
+## Configure a Custom Domain using the Supabase Dashboard
+
+Follow the **Custom Domains** steps in the [General Settings](https://app.supabase.com/project/_/settings/general) page in the Dashboard to set up a custom domain for your project.
+
+## Configure a Custom Domain using the Supabase CLI
+
+This example assumes your Supabase project is `foobarbaz` with a corresponding API URL `foobarbaz.supabase.co` and configures a custom domain at `api.example.com`.
To get started:
-1. [Install](/docs/guides/cli) the Supabase CLI 1.11.3+.
-1. [Log in](/docs/guides/cli/local-development#log-in-to-the-supabase-cli) to your Supabase account using the CLI.
-1. Ensure that you have [Owner or Admin permissions](/docs/guides/platform#manage-team-members) for the project you'd like to set up a custom domain for.
-1. Enable the Custom Domains add-on in the Billing page of the Dashboard.
-
-## Configure a Custom Domain
-
-This example assumes your Supabase project is `foobarbaz` with a corresponding API URL `foobarbaz.supabase.co` and configures a custom domain at `api.example.com`.
+1. [Install](/docs/guides/resources/supabase-cli) the Supabase CLI 1.11.3+.
+1. [Log in](/docs/guides/resources/supabase-cli/local-development#log-in-to-the-supabase-cli) to your Supabase account using the CLI.
### Configure a CNAME
@@ -47,7 +49,7 @@ Required outstanding validation records:
api.example.com TXT -> ca3-F1HvR9i938OgVwpCFwi1jTsbhe1hvT0Ic3efPY3Q
```
-[^1] One of the records requires you to _replace_ the CNAME record set up in [the first step](#cname-configure) with a TXT record. You'll be able to restore it back to the CNAME after the verification process has been completed.
+[^1] One of the records requires you to _replace_ the CNAME record set up in [the first step](#configure-a-cname) with a TXT record. You'll be able to restore it back to the CNAME after the verification process has been completed.
### Verify your domain
@@ -97,9 +99,9 @@ Vanity Subdomains allow you to present a basic branded experience, compared to c
To get started:
-1. [Install](/docs/guides/cli) the Supabase CLI 1.22.0+.
-1. [Log in](/docs/guides/cli/local-development#log-in-to-the-supabase-cli) to your Supabase account using the CLI.
-1. Ensure that you have [Owner or Admin permissions](/docs/guides/hosting/platform#manage-team-members) for the project you'd like to set up a vanity subdomain for.
+1. [Install](/docs/guides/resources/supabase-cli) the Supabase CLI 1.22.0+.
+1. [Log in](/docs/guides/resources/supabase-cli/local-development#log-in-to-the-supabase-cli) to your Supabase account using the CLI.
+1. Ensure that you have [Owner or Admin permissions](/docs/guides/platform/access-control#manage-team-members) for the project you'd like to set up a vanity subdomain for.
1. Ensure that your project has a paid subscription (Pro/Pay as you go/Enterprise tier) in the [Billing page of the Dashboard](https://app.supabase.com/project/_/settings/billing/subscription).
## Configure a Vanity Subdomain
@@ -141,7 +143,8 @@ As with the final activation stage of the process for setting up a vanity subdom
## Limitations
-Edge functions do not honor the custom domain or the vanity subdomain setting and they still have to be invoked via the `foobarbaz.supabase.co` domain.
+- Edge functions do not honor the custom domain or the vanity subdomain setting and they still have to be invoked via the `foobarbaz.supabase.co` domain.
+- A Supabase project can—at this time—use either a Custom Domain or a Vanity Subdomain, but not both.
export const Page = ({ children }) =>
diff --git a/apps/docs/pages/guides/platform/going-into-prod.mdx b/apps/docs/pages/guides/platform/going-into-prod.mdx
index cfb579ce518..73c25d0a1c5 100644
--- a/apps/docs/pages/guides/platform/going-into-prod.mdx
+++ b/apps/docs/pages/guides/platform/going-into-prod.mdx
@@ -54,10 +54,31 @@ After developing your project and deciding it's Production Ready, you should run
- Supabase employs a number of safeguards against bursts of incoming traffic to prevent abuse and help maximize stability across the platform
- If you're expecting high load events including production launches or heavy load testing, or prolonged high resource usage please give us at least 2 weeks notice. You can do this by opening a ticket via the [support form](https://app.supabase.com/support/new).
+### Rate Limits
+
+- The table below shows the rate limit quotas on the following authentication endpoints:
+
+| Endpoint | Path | Limited By | Rate Limit |
+| ------------------------------------------------ | -------------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------- |
+| All endpoints that send emails | `/auth/v1/signup` `/auth/v1/recover` `/auth/v1/user`[^1] | Sum of combined requests | Defaults to 30 emails per hour. Is customizable with custom SMTP set up. |
+| All endpoints that send One-Time-Passwords (OTP) | `/auth/v1/otp` | Sum of combined requests | Defaults to 30 OTPs per hour. Is customizable. |
+| Send OTPs or magiclinks | `/auth/v1/otp` | Last request | Defaults to 60 seconds window before a new request is allowed. Is customizable. |
+| Signup confirmation request | `/auth/v1/signup` | Last request | Defaults to 60 seconds window before a new request is allowed. Is customizable. |
+| Password Reset Request | `/auth/v1/recover` | Last request | Defaults to 60 seconds window before a new request is allowed. Is customizable. |
+| Verification requests | `/auth/v1/verify` | IP Address | 360 requests per hour (with bursts up to 30 requests) |
+| Token refresh requests | `/auth/v1/token` | IP Address | 360 requests per hour (with bursts up to 30 requests) |
+| Create or Verify an MFA challenge | `/auth/v1/factors/:id/challenge` `/auth/v1/factors/:id/verify` | IP Address | 15 requests per minute (with bursts up to 30 requests) |
+
+### Abuse Prevention
+
+- Supabase provides CAPTCHA protection on the signup, sign-in and password reset endpoints. Please refer to [our guide](/docs/guides/auth/auth-captcha) on how to protect against abuse using this method.
+
## Next steps
This checklist is always growing so be sure to check back frequently, and also feel free to suggest additions and amendments by making a PR on [GitHub](https://github.com/supabase/supabase).
+[^1]: The rate limit is only applied on `/auth/v1/user` if this endpoint is called to update the user's email address.
+
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/platform/network-restrictions.mdx b/apps/docs/pages/guides/platform/network-restrictions.mdx
index 7dfde6433cd..c2286c6a4f5 100644
--- a/apps/docs/pages/guides/platform/network-restrictions.mdx
+++ b/apps/docs/pages/guides/platform/network-restrictions.mdx
@@ -20,7 +20,7 @@ To get started:
1. [Install](/docs/guides/cli) the Supabase CLI 1.22.0+.
1. [Log in](/docs/guides/cli/local-development#log-in-to-the-supabase-cli) to your Supabase account using the CLI.
-1. Ensure that you have [Owner or Admin permissions](/docs/guides/hosting/platform#manage-team-members) for the project that you are enabling network restrictions.
+1. Ensure that you have [Owner or Admin permissions](/docs/guides/hosting/platform/access-control#manage-team-members) for the project that you are enabling network restrictions.
## Check restrictions
diff --git a/apps/docs/pages/guides/realtime/postgres-changes.mdx b/apps/docs/pages/guides/realtime/postgres-changes.mdx
index 79a76e884b2..0d68f25c2ef 100644
--- a/apps/docs/pages/guides/realtime/postgres-changes.mdx
+++ b/apps/docs/pages/guides/realtime/postgres-changes.mdx
@@ -63,7 +63,14 @@ const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY
*/
const channel = supabase
.channel('schema-db-changes')
- .on('postgres_changes', { event: '*', schema: 'public' }, (payload) => console.log(payload))
+ .on(
+ 'postgres_changes',
+ {
+ event: '*',
+ schema: 'public',
+ },
+ (payload) => console.log(payload)
+ )
.subscribe()
```
@@ -74,8 +81,14 @@ To listen to changes on a table in the `public` schema:
const channel = supabase
.channel('table-db-changes')
- .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, (payload) =>
- console.log(payload)
+ .on(
+ 'postgres_changes',
+ {
+ event: 'INSERT',
+ schema: 'public',
+ table: 'messages',
+ },
+ (payload) => console.log(payload)
)
.subscribe()
```
@@ -109,11 +122,21 @@ const channel = supabase
.channel('db-changes')
.on(
'postgres_changes',
- { event: '*', schema: 'public', table: 'messages', filter: 'body=eq.bye' },
+ {
+ event: '*',
+ schema: 'public',
+ table: 'messages',
+ filter: 'body=eq.bye',
+ },
(payload) => console.log(payload)
)
- .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'users' }, (payload) =>
- console.log(payload)
+ .on('postgres_changes',
+ {
+ event: 'INSERT',
+ schema: 'public',
+ table: 'users',
+ },
+ (payload) => console.log(payload)
)
.subscribe()
```
diff --git a/apps/docs/pages/guides/resources.mdx b/apps/docs/pages/guides/resources.mdx
index d8934c38ddc..b1b139020d3 100644
--- a/apps/docs/pages/guides/resources.mdx
+++ b/apps/docs/pages/guides/resources.mdx
@@ -76,16 +76,6 @@ export const meta = {
-
-
- ### Self-Hosting Supabase
-
- We're happy to host your next big thing, but we also make it easy to host Supabase on your own. We use Docker to help make setup as painless as possible.
-
-[Learn more](/docs/guides/resources/self-hosting)
-
-
-
{/* end of container */}
@@ -112,25 +102,25 @@ export const migrationGuides = [
{
title: 'Firebase Auth',
icon: '/docs/img/icons/firebase-icon',
- href: '/guides/migrations/firebase-auth',
+ href: '/guides/resources/migrating-to-supabase/firebase-auth',
description: 'Move your auth users from a Firebase project to a Supabase project.',
},
{
title: 'Firestore Data',
icon: '/docs/img/icons/firebase-icon',
- href: '/guides/migrations/firestore-data',
+ href: '/guides/resources/migrating-to-supabase/firestore-data',
description: 'Migrate the contents of a Firestore collection to a single PostgreSQL table.',
},
{
title: 'Firebase Storage',
icon: '/docs/img/icons/firebase-icon',
- href: '/guides/migrations/firebase-storage',
+ href: '/guides/resources/migrating-to-supabase/firebase-storage',
description: 'Convert your Firebase Storage files to Supabase Storage.',
},
{
title: 'Heroku',
icon: '/docs/img/icons/heroku-icon',
- href: '/guides/migrations/heroku',
+ href: '/guides/resources/migrating-to-supabase/heroku',
description: 'Migrate your Heroku Postgres database to Supabase.',
},
]
diff --git a/apps/docs/pages/guides/resources/supabase-cli/local-development.mdx b/apps/docs/pages/guides/resources/supabase-cli/local-development.mdx
index 4586927a3f4..3ff1cb1c82a 100644
--- a/apps/docs/pages/guides/resources/supabase-cli/local-development.mdx
+++ b/apps/docs/pages/guides/resources/supabase-cli/local-development.mdx
@@ -173,7 +173,7 @@ Use the seed script in `supabase/seed.sql` (created with [`supabase init`](/docs
-- in supabase/seed.sql
insert into public.employees (name)
values
- ('Erlich Backman'),
+ ('Erlich Bachman'),
('Richard Hendricks'),
('Monica Hall');
```
diff --git a/apps/docs/pages/guides/resources/self-hosting.mdx b/apps/docs/pages/guides/self-hosting.mdx
similarity index 96%
rename from apps/docs/pages/guides/resources/self-hosting.mdx
rename to apps/docs/pages/guides/self-hosting.mdx
index 51e618c4d31..3092dd9cf45 100644
--- a/apps/docs/pages/guides/resources/self-hosting.mdx
+++ b/apps/docs/pages/guides/self-hosting.mdx
@@ -1,16 +1,14 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'overview',
- title: 'Self Hosting',
- sidebar_label: 'Overview',
- description: 'Getting started with Self Hosting.',
+ title: 'Self-Hosting',
+ description: 'Getting started with self-hosting Supabase.',
}
There are several ways to use Supabase:
- [Supabase Cloud](https://app.supabase.com): you don't need to deploy anything. We will manage and scale your infrastructure.
-- [Docker](../../guides/hosting/docker): deploy to your own infrastructure.
+- [Docker](/docs/guides/self-hosting/docker): deploy to your own infrastructure.
- Kubernetes: coming soon.
## Architecture
@@ -151,7 +149,7 @@ We realize that database migrations are difficult, and this is one of the proble
While Supabase officially supports Docker, we have several other deployment strategies managed by the community:
-- [supabase-docker](../../guides/hosting/docker) (Official)
+- [supabase-docker](/docs/guides/self-hosting/docker) (Official)
- [supabase-kubernetes](https://github.com/supabase-community/supabase-kubernetes) (Unofficial)
- [supabase-terraform](https://github.com/supabase-community/supabase-terraform) (Unofficial)
- [supabase-traefik](https://github.com/supabase-community/supabase-traefik) (Unofficial)
diff --git a/apps/docs/pages/guides/self-hosting/auth/config.mdx b/apps/docs/pages/guides/self-hosting/auth/config.mdx
new file mode 100644
index 00000000000..0e894114c65
--- /dev/null
+++ b/apps/docs/pages/guides/self-hosting/auth/config.mdx
@@ -0,0 +1,38 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+import { getAuthConfigV1 } from '~/lib/mdx/getConfig'
+import Param from '~/components/Params'
+
+export const spec = getAuthConfigV1()
+
+export const meta = {
+ title: 'Auth Self-hosting Config',
+ description: 'How to configure and deploy Supabase.',
+}
+
+
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/resources/self-hosting/docker.mdx b/apps/docs/pages/guides/self-hosting/docker.mdx
similarity index 78%
rename from apps/docs/pages/guides/resources/self-hosting/docker.mdx
rename to apps/docs/pages/guides/self-hosting/docker.mdx
index db9bcdf0563..0a32049889b 100644
--- a/apps/docs/pages/guides/resources/self-hosting/docker.mdx
+++ b/apps/docs/pages/guides/self-hosting/docker.mdx
@@ -1,10 +1,8 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
- id: 'docker',
- title: 'Self-hosting with Docker',
- description: 'How to configure and deploy Supabase.',
- sidebar_label: 'Docker',
+ title: 'Self-Hosting with Docker',
+ description: 'Learn how to configure and deploy Supabase with Docker.',
}
Docker is the easiest way to get started with self-hosted Supabase.
@@ -13,7 +11,7 @@ Docker is the easiest way to get started with self-hosted Supabase.
You need the following installed in your system:
-- [Docker](https://docs.docker.com/engine/install/) and [docker-compose](https://docs.docker.com/compose/install/)
+- [Docker](https://docs.docker.com/engine/install/) and [docker compose](https://docs.docker.com/compose/install/)
- [Git](https://git-scm.com/downloads)
## Quick Start
@@ -33,7 +31,7 @@ cd supabase/docker
cp .env.example .env
# Start
-docker-compose up
+docker compose up
```
Now visit [http://localhost:3000](http://localhost:3000) to start using Supabase Studio.
@@ -42,11 +40,11 @@ Now visit [http://localhost:3000](http://localhost:3000) to start using Supabase
While we provided you with some example secrets for getting started, you should NEVER deploy your Supabase setup using the defaults we have provided.
-Please follow these steps to secure your Docker setup. We [strongly recommend](../../guides/hosting/overview#managing-your-secrets) using a secrets manager when deploying to production.
+Follow these steps to secure your Docker setup. We strongly recommend using a [secrets manager](../self-hosting#managing-your-secrets) when deploying to production.
### Generate API Keys
-Use your `JWT_SECRET` to generate a `anon` and `service` API keys using the [JWT generator](../../guides/hosting/overview#api-keys).
+Use your `JWT_SECRET` to generate a `anon` and `service` API keys using the [JWT generator](../self-hosting#api-keys).
Replace the values in these files:
@@ -72,7 +70,7 @@ The Docker setup doesn't include a management database for managing users and lo
## Configuration
-Each system can be [configured](../../guides/hosting/overview#configuration) to suit your particular use-case.
+Each system can be [configured](../self-hosting#configuration) to suit your particular use-case.
To keep the setup simple, we made some choices that may not be optimal for production:
@@ -82,7 +80,7 @@ To keep the setup simple, we made some choices that may not be optimal for produ
### Using an external database
-We strongly [recommend](../../guides/hosting/overview#managing-your-database) that you decouple your database from `docker-compose` before deploying.
+We strongly recommend [decoupling your database](../self-hosting#managing-your-database) from `docker-compose` before deploying.
The middleware will run with any PostgreSQL database that has logical replication enabled. The following environment variables should be updated
in the `.env` file to point to your external database:
@@ -99,9 +97,13 @@ Once you have done this, you can safely comment out the `db` section of the `doc
### Setting database's `log_min_messages`
-By default, `docker-compose` sets the database's `log_min_messages` configuration to `fatal` to prevent redundant logs generated by Realtime.
+By default, `docker compose` sets the database's `log_min_messages` configuration to `fatal` to prevent redundant logs generated by Realtime.
However, you might miss important log messages such as database errors. Configure `log_min_messages` based on your needs.
+### File storage backend on macOS
+
+By default, Storage backend is set to `file`, which is to use local files as the storage backend. To make it work on macOS, you need to choose `VirtioFS` as the Docker container file sharing implementation (in Docker Desktop -> Preferences -> General).
+
## Deploying
See the following guides to deploy Docker Compose setup using your preferred tool and platform:
diff --git a/apps/docs/pages/guides/self-hosting/realtime/config.mdx b/apps/docs/pages/guides/self-hosting/realtime/config.mdx
new file mode 100644
index 00000000000..a52dc2c111a
--- /dev/null
+++ b/apps/docs/pages/guides/self-hosting/realtime/config.mdx
@@ -0,0 +1,38 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+import { getRealtimeConfigV0 } from '~/lib/mdx/getConfig'
+import Param from '~/components/Params'
+
+export const spec = getRealtimeConfigV0()
+
+export const meta = {
+ title: 'Realtime Self-hosting Config',
+ description: 'How to configure and deploy Supabase.',
+}
+
+
- As a database company, being SOC2 compliant is important when handling sensitive
- customer data. Supabase is currently SOC2 Type 1 compliant and we're also working on
- getting certified for SOC2 Type 2 and HIPAA next.
-
-
-
You can request for our latest SOC 2 document by submitting your request here.
@@ -185,6 +185,6 @@ Supabase হল ওপেন সোর্স টুলের সংমিশ্
- [অনুবাদের তালিকা](/i18n/languages.md)
-## স্পনসর
+## পৃষ্ঠপোষক
-[](https://github.com/sponsors/supabase)
+[](https://github.com/sponsors/supabase)
diff --git a/package-lock.json b/package-lock.json
index f0df444fb07..d002b6210bb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11822,6 +11822,80 @@
"npm": ">=5"
}
},
+ "node_modules/aws-sdk": {
+ "version": "2.1275.0",
+ "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1275.0.tgz",
+ "integrity": "sha512-GP7oS0BTh012pAMnNSVVQQrhfubeLH2moLUQQiuk9cPrqQYeP74BU3nnALM30CoFNzX+DwLanOeFU+KMfUGLHg==",
+ "dev": true,
+ "dependencies": {
+ "buffer": "4.9.2",
+ "events": "1.1.1",
+ "ieee754": "1.1.13",
+ "jmespath": "0.16.0",
+ "querystring": "0.2.0",
+ "sax": "1.2.1",
+ "url": "0.10.3",
+ "util": "^0.12.4",
+ "uuid": "8.0.0",
+ "xml2js": "0.4.19"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/aws-sdk/node_modules/events": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+ "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/aws-sdk/node_modules/ieee754": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
+ "dev": true
+ },
+ "node_modules/aws-sdk/node_modules/punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==",
+ "dev": true
+ },
+ "node_modules/aws-sdk/node_modules/url": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
+ "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ }
+ },
+ "node_modules/aws-sdk/node_modules/util": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+ "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
+ "node_modules/aws-sdk/node_modules/uuid": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz",
+ "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==",
+ "dev": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/axe-core": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz",
@@ -20160,6 +20234,21 @@
"node": ">=6"
}
},
+ "node_modules/is-generator-function": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+ "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "dev": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -22379,6 +22468,15 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
+ "node_modules/jmespath": {
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
+ "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
"node_modules/join-component": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz",
@@ -32401,6 +32499,12 @@
"node": ">=12.0.0"
}
},
+ "node_modules/sax": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
+ "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==",
+ "dev": true
+ },
"node_modules/saxes": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
@@ -36895,6 +36999,25 @@
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
"dev": true
},
+ "node_modules/xml2js": {
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
+ "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
+ "dev": true,
+ "dependencies": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~9.0.1"
+ }
+ },
+ "node_modules/xmlbuilder": {
+ "version": "9.0.7",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+ "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
"node_modules/xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
@@ -37484,6 +37607,7 @@
"uniforms-bootstrap4": "^3.6.2",
"uniforms-bridge-json-schema": "^3.6.2",
"uuid": "^8.3.2",
+ "valtio": "^1.7.6",
"yup": "^0.32.11",
"yup-password": "^0.2.2",
"zxcvbn": "^4.4.2"
@@ -37522,6 +37646,7 @@
"@types/uuid": "^8.3.4",
"@types/zxcvbn": "^4.4.1",
"autoprefixer": "10.4.2",
+ "aws-sdk": "^2.1275.0",
"babel-loader": "^8.2.3",
"common": "*",
"config": "*",
@@ -37705,6 +37830,11 @@
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-9.3.1.tgz",
"integrity": "sha512-/Yvsd2D7miYB4HLJ3hOOS0+vnowQpaT75FsHzr/y5M9P4q9bwa7RcbW2YdH6KZBn8ceLbKGnHxMZ1CHliGHUFw=="
},
+ "studio/node_modules/proxy-compare": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.3.0.tgz",
+ "integrity": "sha512-c3L2CcAi7f7pvlD0D7xsF+2CQIW8C3HaYx2Pfgq8eA4HAl3GAH6/dVYsyBbYF/0XJs2ziGLrzmz5fmzPm6A0pQ=="
+ },
"studio/node_modules/react-resize-detector": {
"version": "6.7.8",
"resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-6.7.8.tgz",
@@ -37765,6 +37895,46 @@
"node": ">=4.2.0"
}
},
+ "studio/node_modules/valtio": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.7.6.tgz",
+ "integrity": "sha512-zsGrCCYOIpy8egQxftduFyJusF/BMu3CganhHKUOE/I6t6V6yA1MDfZZkrYoWYCGkC3rSBYcIHEEsoYQM9lV2w==",
+ "dependencies": {
+ "proxy-compare": "2.3.0",
+ "use-sync-external-store": "1.2.0"
+ },
+ "engines": {
+ "node": ">=12.7.0"
+ },
+ "peerDependencies": {
+ "@babel/helper-module-imports": ">=7.12",
+ "@babel/types": ">=7.13",
+ "aslemammad-vite-plugin-macro": ">=1.0.0-alpha.1",
+ "babel-plugin-macros": ">=3.0",
+ "react": ">=16.8",
+ "vite": ">=2.8.6"
+ },
+ "peerDependenciesMeta": {
+ "@babel/helper-module-imports": {
+ "optional": true
+ },
+ "@babel/types": {
+ "optional": true
+ },
+ "aslemammad-vite-plugin-macro": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
"studio/node_modules/webpack-bundle-analyzer": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz",
@@ -46055,6 +46225,73 @@
"awesome-imperative-promise": "^1.0.1"
}
},
+ "aws-sdk": {
+ "version": "2.1275.0",
+ "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1275.0.tgz",
+ "integrity": "sha512-GP7oS0BTh012pAMnNSVVQQrhfubeLH2moLUQQiuk9cPrqQYeP74BU3nnALM30CoFNzX+DwLanOeFU+KMfUGLHg==",
+ "dev": true,
+ "requires": {
+ "buffer": "4.9.2",
+ "events": "1.1.1",
+ "ieee754": "1.1.13",
+ "jmespath": "0.16.0",
+ "querystring": "0.2.0",
+ "sax": "1.2.1",
+ "url": "0.10.3",
+ "util": "^0.12.4",
+ "uuid": "8.0.0",
+ "xml2js": "0.4.19"
+ },
+ "dependencies": {
+ "events": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+ "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==",
+ "dev": true
+ },
+ "ieee754": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==",
+ "dev": true
+ },
+ "url": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
+ "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==",
+ "dev": true,
+ "requires": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ }
+ },
+ "util": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+ "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
+ "uuid": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz",
+ "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==",
+ "dev": true
+ }
+ }
+ },
"axe-core": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz",
@@ -53030,6 +53267,15 @@
"integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
"dev": true
},
+ "is-generator-function": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+ "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+ "dev": true,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
"is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -54820,6 +55066,12 @@
}
}
},
+ "jmespath": {
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
+ "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
+ "dev": true
+ },
"join-component": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz",
@@ -62150,6 +62402,12 @@
"source-map-js": ">=0.6.2 <2.0.0"
}
},
+ "sax": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
+ "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==",
+ "dev": true
+ },
"saxes": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
@@ -63095,6 +63353,7 @@
"analytics-node": "^3.5.0",
"autoprefixer": "10.4.2",
"awesome-debounce-promise": "^2.1.0",
+ "aws-sdk": "^2.1275.0",
"babel-loader": "^8.2.3",
"blueimp-md5": "^2.19.0",
"clipboard": "^2.0.8",
@@ -63158,6 +63417,7 @@
"uniforms-bootstrap4": "^3.6.2",
"uniforms-bridge-json-schema": "^3.6.2",
"uuid": "^8.3.2",
+ "valtio": "^1.7.6",
"yup": "^0.32.11",
"yup-password": "^0.2.2",
"zxcvbn": "^4.4.2"
@@ -63308,6 +63568,11 @@
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-9.3.1.tgz",
"integrity": "sha512-/Yvsd2D7miYB4HLJ3hOOS0+vnowQpaT75FsHzr/y5M9P4q9bwa7RcbW2YdH6KZBn8ceLbKGnHxMZ1CHliGHUFw=="
},
+ "proxy-compare": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.3.0.tgz",
+ "integrity": "sha512-c3L2CcAi7f7pvlD0D7xsF+2CQIW8C3HaYx2Pfgq8eA4HAl3GAH6/dVYsyBbYF/0XJs2ziGLrzmz5fmzPm6A0pQ=="
+ },
"react-resize-detector": {
"version": "6.7.8",
"resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-6.7.8.tgz",
@@ -63351,6 +63616,15 @@
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
"dev": true
},
+ "valtio": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.7.6.tgz",
+ "integrity": "sha512-zsGrCCYOIpy8egQxftduFyJusF/BMu3CganhHKUOE/I6t6V6yA1MDfZZkrYoWYCGkC3rSBYcIHEEsoYQM9lV2w==",
+ "requires": {
+ "proxy-compare": "2.3.0",
+ "use-sync-external-store": "1.2.0"
+ }
+ },
"webpack-bundle-analyzer": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz",
@@ -66375,6 +66649,22 @@
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
"dev": true
},
+ "xml2js": {
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
+ "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
+ "dev": true,
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~9.0.1"
+ }
+ },
+ "xmlbuilder": {
+ "version": "9.0.7",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+ "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==",
+ "dev": true
+ },
"xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
diff --git a/package.json b/package.json
index 4a605d28309..bf893aa1978 100644
--- a/package.json
+++ b/package.json
@@ -22,10 +22,10 @@
"dev:www": "turbo run dev --filter=www --parallel",
"lint": "turbo run lint",
"format": "prettier --write \"apps/**/*.{js,jsx,ts,tsx,css,md,json}\"",
- "docker:dev": "cd docker && docker-compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up --renew-anon-volumes",
- "docker:up": "cd docker && docker-compose up",
- "docker:down": "cd docker && docker-compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down --remove-orphans",
- "docker:remove": "cd docker && docker-compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml rm -vfs",
+ "docker:dev": "cd docker && docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up --renew-anon-volumes",
+ "docker:up": "cd docker && docker compose up",
+ "docker:down": "cd docker && docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down --remove-orphans",
+ "docker:remove": "cd docker && docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml rm -vfs",
"test": "jest",
"test:studio": "turbo run test --filter=studio",
"perf:kong": "ab -t 5 -c 20 -T application/json http://localhost:8000/",
diff --git a/spec/cli_v1_commands.yaml b/spec/cli_v1_commands.yaml
index e0799ffd7c0..fabffeb3733 100644
--- a/spec/cli_v1_commands.yaml
+++ b/spec/cli_v1_commands.yaml
@@ -1,7 +1,7 @@
clispec: '001'
info:
id: cli
- version: 1.24.0
+ version: 1.27.0
title: Supabase CLI
language: sh
source: https://github.com/supabase/cli
@@ -58,6 +58,11 @@ commands:
usage: supabase vanity-subdomains get
subcommands: []
flags:
+ - id: experimental
+ name: --experimental
+ description: enable experimental features
+ required: true
+ default_value: 'false'
- id: project-ref
name: --project-ref
description: Project ref of the Supabase project.
@@ -72,6 +77,11 @@ commands:
usage: supabase vanity-subdomains delete
subcommands: []
flags:
+ - id: experimental
+ name: --experimental
+ description: enable experimental features
+ required: true
+ default_value: 'false'
- id: project-ref
name: --project-ref
description: Project ref of the Supabase project.
@@ -89,6 +99,11 @@ commands:
description: |
The desired vanity subdomain to use for your Supabase project.
default_value: ''
+ - id: experimental
+ name: --experimental
+ description: enable experimental features
+ required: true
+ default_value: 'false'
- id: project-ref
name: --project-ref
description: Project ref of the Supabase project.
@@ -111,6 +126,11 @@ commands:
description: |
The desired vanity subdomain to use for your Supabase project.
default_value: ''
+ - id: experimental
+ name: --experimental
+ description: enable experimental features
+ required: true
+ default_value: 'false'
- id: project-ref
name: --project-ref
description: Project ref of the Supabase project.
@@ -372,6 +392,11 @@ commands:
name: --db-allow-cidr
description: CIDR to allow DB connections from.
default_value: '[]'
+ - id: experimental
+ name: --experimental
+ description: enable experimental features
+ required: true
+ default_value: 'false'
- id: project-ref
name: --project-ref
description: Project ref of the Supabase project.
@@ -384,6 +409,63 @@ commands:
usage: supabase network-restrictions get
subcommands: []
flags:
+ - id: experimental
+ name: --experimental
+ description: enable experimental features
+ required: true
+ default_value: 'false'
+ - id: project-ref
+ name: --project-ref
+ description: Project ref of the Supabase project.
+ default_value: ''
+ - id: supabase-network-bans
+ title: supabase network-bans
+ summary: Manage network bans
+ description: |-
+ Network bans are IPs that get temporarily blocked if their traffic pattern looks abusive (e.g. multiple failed auth attempts).
+
+ The subcommands help you view the current bans, and unblock IPs if desired.
+ tags:
+ - management-api
+ links: []
+ subcommands:
+ - supabase-network-bans-get
+ - supabase-network-bans-remove
+ flags: []
+ - id: supabase-network-bans-remove
+ title: supabase network-bans remove
+ summary: Remove a network ban
+ tags: []
+ links: []
+ usage: supabase network-bans remove [flags]
+ subcommands: []
+ flags:
+ - id: db-unban-ip
+ name: --db-unban-ip
+ description: IP to allow DB connections from.
+ default_value: '[]'
+ - id: experimental
+ name: --experimental
+ description: enable experimental features
+ required: true
+ default_value: 'false'
+ - id: project-ref
+ name: --project-ref
+ description: Project ref of the Supabase project.
+ default_value: ''
+ - id: supabase-network-bans-get
+ title: supabase network-bans get
+ summary: Get the current network bans
+ tags: []
+ links: []
+ usage: supabase network-bans get
+ subcommands: []
+ flags:
+ - id: experimental
+ name: --experimental
+ description: enable experimental features
+ required: true
+ default_value: 'false'
- id: project-ref
name: --project-ref
description: Project ref of the Supabase project.
@@ -407,6 +489,10 @@ commands:
usage: supabase migration repair [flags]
subcommands: []
flags:
+ - id: password
+ name: -p, --password
+ description: Password to your remote Postgres database.
+ default_value: ''
- id: status
name: --status <[ applied | reverted ]>
description: Version status to update.
@@ -722,6 +808,7 @@ commands:
links: []
subcommands:
- supabase-db-diff
+ - supabase-db-dump
- supabase-db-lint
- supabase-db-push
- supabase-db-remote
@@ -804,6 +891,22 @@ commands:
name: -s, --schema
description: List of schema to include.
default_value: '[public]'
+ - id: supabase-db-dump
+ title: supabase db dump
+ summary: Dumps schemas from the remote database
+ tags: []
+ links: []
+ usage: supabase db dump [flags]
+ subcommands: []
+ flags:
+ - id: file
+ name: -f, --file
+ description: File path to save the dumped schema.
+ default_value: ''
+ - id: password
+ name: -p, --password
+ description: Password to your remote Postgres database.
+ default_value: ''
- id: supabase-db-diff
title: supabase db diff
summary: Diffs the local database for schema changes
@@ -814,7 +917,7 @@ commands:
flags:
- id: file
name: -f, --file
- description: Saves schema diff to a file.
+ description: Saves schema diff to a new migration file.
default_value: ''
- id: linked
name: --linked
diff --git a/spec/common-cli-sections.json b/spec/common-cli-sections.json
index 9ce2b34c7e0..4481a05bacf 100644
--- a/spec/common-cli-sections.json
+++ b/spec/common-cli-sections.json
@@ -17,33 +17,9 @@
"title": "general",
"items": [
{
- "id": "supabase-test",
- "title": "supabase test",
- "slug": "supabase-test",
- "type": "cli-command"
- },
- {
- "id": "supabase-test-db",
- "title": "supabase test db",
- "slug": "supabase-test-db",
- "type": "cli-command"
- },
- {
- "id": "supabase-stop",
- "title": "supabase stop",
- "slug": "supabase-stop",
- "type": "cli-command"
- },
- {
- "id": "supabase-status",
- "title": "supabase status",
- "slug": "supabase-status",
- "type": "cli-command"
- },
- {
- "id": "supabase-start",
- "title": "supabase start",
- "slug": "supabase-start",
+ "id": "supabase-init",
+ "title": "supabase init",
+ "slug": "supabase-init",
"type": "cli-command"
},
{
@@ -59,9 +35,33 @@
"type": "cli-command"
},
{
- "id": "supabase-init",
- "title": "supabase init",
- "slug": "supabase-init",
+ "id": "supabase-start",
+ "title": "supabase start",
+ "slug": "supabase-start",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-stop",
+ "title": "supabase stop",
+ "slug": "supabase-stop",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-status",
+ "title": "supabase status",
+ "slug": "supabase-status",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-test",
+ "title": "supabase test",
+ "slug": "supabase-test",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-test-db",
+ "title": "supabase test db",
+ "slug": "supabase-test-db",
"type": "cli-command"
},
{
@@ -88,12 +88,36 @@
"title": "Database",
"items": [
{ "id": "supabase-db", "title": "supabase db", "slug": "supabase-db", "type": "cli-command" },
+ {
+ "id": "supabase-db-dump",
+ "title": "supabase db dump",
+ "slug": "supabase-db-dump",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-db-push",
+ "title": "supabase db push",
+ "slug": "supabase-db-push",
+ "type": "cli-command"
+ },
{
"id": "supabase-db-reset",
"title": "supabase db reset",
"slug": "supabase-db-reset",
"type": "cli-command"
},
+ {
+ "id": "supabase-db-diff",
+ "title": "supabase db diff",
+ "slug": "supabase-db-diff",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-db-lint",
+ "title": "supabase db lint",
+ "slug": "supabase-db-lint",
+ "type": "cli-command"
+ },
{
"id": "supabase-db-remote",
"title": "supabase db remote",
@@ -105,24 +129,6 @@
"title": "supabase db remote commit",
"slug": "supabase-db-remote-commit",
"type": "cli-command"
- },
- {
- "id": "supabase-db-push",
- "title": "supabase db push",
- "slug": "supabase-db-push",
- "type": "cli-command"
- },
- {
- "id": "supabase-db-lint",
- "title": "supabase db lint",
- "slug": "supabase-db-lint",
- "type": "cli-command"
- },
- {
- "id": "supabase-db-diff",
- "title": "supabase db diff",
- "slug": "supabase-db-diff",
- "type": "cli-command"
}
]
},
@@ -155,35 +161,6 @@
}
]
},
- {
- "title": "secrets",
- "items": [
- {
- "id": "supabase-secrets",
- "title": "supabase secrets",
- "slug": "supabase-secrets",
- "type": "cli-command"
- },
- {
- "id": "supabase-secrets-unset",
- "title": "supabase secrets unset",
- "slug": "supabase-secrets-unset",
- "type": "cli-command"
- },
- {
- "id": "supabase-secrets-set",
- "title": "supabase secrets set",
- "slug": "supabase-secrets-set",
- "type": "cli-command"
- },
- {
- "id": "supabase-secrets-list",
- "title": "supabase secrets list",
- "slug": "supabase-secrets-list",
- "type": "cli-command"
- }
- ]
- },
{
"title": "projects",
"items": [
@@ -193,17 +170,17 @@
"slug": "supabase-projects",
"type": "cli-command"
},
- {
- "id": "supabase-projects-list",
- "title": "supabase projects list",
- "slug": "supabase-projects-list",
- "type": "cli-command"
- },
{
"id": "supabase-projects-create",
"title": "supabase projects create",
"slug": "supabase-projects-create",
"type": "cli-command"
+ },
+ {
+ "id": "supabase-projects-list",
+ "title": "supabase projects list",
+ "slug": "supabase-projects-list",
+ "type": "cli-command"
}
]
},
@@ -233,18 +210,18 @@
"slug": "supabase-functions",
"type": "cli-command"
},
- {
- "id": "supabase-functions-serve",
- "title": "supabase functions serve",
- "slug": "supabase-functions-serve",
- "type": "cli-command"
- },
{
"id": "supabase-functions-new",
"title": "supabase functions new",
"slug": "supabase-functions-new",
"type": "cli-command"
},
+ {
+ "id": "supabase-functions-serve",
+ "title": "supabase functions serve",
+ "slug": "supabase-functions-serve",
+ "type": "cli-command"
+ },
{
"id": "supabase-functions-deploy",
"title": "supabase functions deploy",
@@ -259,6 +236,35 @@
}
]
},
+ {
+ "title": "secrets",
+ "items": [
+ {
+ "id": "supabase-secrets",
+ "title": "supabase secrets",
+ "slug": "supabase-secrets",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-secrets-set",
+ "title": "supabase secrets set",
+ "slug": "supabase-secrets-set",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-secrets-list",
+ "title": "supabase secrets list",
+ "slug": "supabase-secrets-list",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-secrets-unset",
+ "title": "supabase secrets unset",
+ "slug": "supabase-secrets-unset",
+ "type": "cli-command"
+ }
+ ]
+ },
{
"title": "Domains",
"items": [
@@ -269,21 +275,9 @@
"type": "cli-command"
},
{
- "id": "supabase-domains-reverify",
- "title": "supabase domains reverify",
- "slug": "supabase-domains-reverify",
- "type": "cli-command"
- },
- {
- "id": "supabase-domains-get",
- "title": "supabase domains get",
- "slug": "supabase-domains-get",
- "type": "cli-command"
- },
- {
- "id": "supabase-domains-delete",
- "title": "supabase domains delete",
- "slug": "supabase-domains-delete",
+ "id": "supabase-domains-activate",
+ "title": "supabase domains activate",
+ "slug": "supabase-domains-activate",
"type": "cli-command"
},
{
@@ -293,9 +287,21 @@
"type": "cli-command"
},
{
- "id": "supabase-domains-activate",
- "title": "supabase domains activate",
- "slug": "supabase-domains-activate",
+ "id": "supabase-domains-get",
+ "title": "supabase domains get",
+ "slug": "supabase-domains-get",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-domains-reverify",
+ "title": "supabase domains reverify",
+ "slug": "supabase-domains-reverify",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-domains-delete",
+ "title": "supabase domains delete",
+ "slug": "supabase-domains-delete",
"type": "cli-command"
}
]
@@ -309,28 +315,51 @@
"slug": "supabase-vanity-subdomains",
"type": "cli-command"
},
- {
- "id": "supabase-vanity-subdomains-check-availability",
- "title": "supabase vanity-subdomains check-availability",
- "slug": "supabase-vanity-subdomains-check-availability",
- "type": "cli-command"
- },
{
"id": "supabase-vanity-subdomains-activate",
"title": "supabase vanity-subdomains activate",
"slug": "supabase-vanity-subdomains-activate",
"type": "cli-command"
},
+ {
+ "id": "supabase-vanity-subdomains-get",
+ "title": "supabase vanity-subdomains get",
+ "slug": "supabase-vanity-subdomains-get",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-vanity-subdomains-check-availability",
+ "title": "supabase vanity-subdomains check-availability",
+ "slug": "supabase-vanity-subdomains-check-availability",
+ "type": "cli-command"
+ },
{
"id": "supabase-vanity-subdomains-delete",
"title": "supabase vanity-subdomains delete",
"slug": "supabase-vanity-subdomains-delete",
"type": "cli-command"
+ }
+ ]
+ },
+ {
+ "title": "Network Bans",
+ "items": [
+ {
+ "id": "supabase-network-bans",
+ "title": "supabase network-bans",
+ "slug": "supabase-network-bans",
+ "type": "cli-command"
},
{
- "id": "supabase-vanity-subdomains-get",
- "title": "supabase vanity-subdomains get",
- "slug": "supabase-vanity-subdomains-get",
+ "id": "supabase-network-bans-get",
+ "title": "supabase network-bans get",
+ "slug": "supabase-network-bans-get",
+ "type": "cli-command"
+ },
+ {
+ "id": "supabase-network-bans-remove",
+ "title": "supabase network-bans remove",
+ "slug": "supabase-network-bans-remove",
"type": "cli-command"
}
]
diff --git a/spec/common-client-libs-sections.json b/spec/common-client-libs-sections.json
index 7aa270c1eaa..f1afe0b46b5 100644
--- a/spec/common-client-libs-sections.json
+++ b/spec/common-client-libs-sections.json
@@ -17,8 +17,7 @@
"title": "Initializing",
"id": "initializing",
"slug": "initializing",
- "type": "markdown",
- "libs": ["dart", "dart_v0"]
+ "type": "function"
},
{
"title": "Typescript Support",
diff --git a/spec/common-self-hosting-auth-sections.json b/spec/common-self-hosting-auth-sections.json
index aac1b124ada..82b090de047 100644
--- a/spec/common-self-hosting-auth-sections.json
+++ b/spec/common-self-hosting-auth-sections.json
@@ -123,12 +123,6 @@
"title": "Returns the updated user",
"type": "operation"
},
- {
- "id": "verifies-a-sign-up",
- "slug": "verifies-a-sign-up",
- "title": "Verifies a sign up",
- "type": "operation"
- },
{
"id": "verifies-a-sign-up",
"slug": "verifies-a-sign-up",
diff --git a/spec/supabase_js_v1.yml b/spec/supabase_js_v1.yml
index c61976c9743..b56b1390eac 100644
--- a/spec/supabase_js_v1.yml
+++ b/spec/supabase_js_v1.yml
@@ -16,6 +16,68 @@ info:
version: '0.0.1'
functions:
+ - id: initializing
+ title: 'Initializing'
+ $ref: '@supabase/supabase-js.index.SupabaseClient.constructor'
+ examples:
+ - id: create-client
+ name: Create Client
+ code: |
+ ```js
+ import { createClient } from '@supabase/supabase-js'
+
+ // Create a single supabase client for interacting with your database
+ const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key')
+ ```
+ - id: with-additional-parameters
+ name: With Additional Parameters
+ code: |
+ ```js
+ import { createClient } from '@supabase/supabase-js'
+
+ const options = {
+ schema: 'public',
+ headers: { 'x-my-custom-header': 'my-app-name' },
+ autoRefreshToken: true,
+ persistSession: true,
+ detectSessionInUrl: true,
+ }
+ const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key', options)
+ ```
+ - id: api-schemas
+ name: API schemas
+ code: |
+ ```js
+ import { createClient } from '@supabase/supabase-js'
+
+ const options = {
+ schema: 'public',
+ headers: { 'x-my-custom-header': 'my-app-name' },
+ autoRefreshToken: true,
+ persistSession: true,
+ detectSessionInUrl: true,
+ }
+ const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key', options)
+ ```
+ description: |
+ By default the API server points to the `public` schema. You can enable other database schemas within the Dashboard.
+ Go to `Settings > API > Schema` and add the schema which you want to expose to the API. You also need to grant `USAGE` on your new schema with the grants you desire, such as `SELECT, INSERT, UPDATE, DELETE`.
+
+ 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
+ name: Custom Fetch Implementation
+ code: |
+ ```js
+ import { createClient } from '@supabase/supabase-js'
+
+ const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key', {
+ fetch: fetch.bind(globalThis),
+ })
+ ```
+ description: |
+ `supabase-js` uses the [`cross-fetch`](https://www.npmjs.com/package/cross-fetch) library to make HTTP requests,
+ but an alternative `fetch` implementation can be provided as an option.
+ This is most useful in environments where `cross-fetch` is not compatible (for instance Cloudflare Workers).
- id: sign-up
title: 'signUp()'
$ref: '@supabase/gotrue-js.GoTrueClient.signUp'
@@ -134,7 +196,9 @@ 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 [see Auth>Settings in dashboard], or must match one of the Additional Redirect URLs [also in Auth>Settings]).
+ 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).
+
+ See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
code: |
```js
const { user, session, error } = await supabase.auth.signIn({
@@ -502,8 +566,10 @@ functions:
$ref: '@supabase/gotrue-js.GoTrueApi.resetPasswordForEmail'
notes: |
Sends a password reset request to an email address.
- When the user clicks the reset link in the email they are redirected back to your application.
- Prompt the user for a new password and call `auth.update()`:
+ - When the user clicks the reset link in the email they are redirected back to your application.
+ You can configure the URL that the user is redirected to via the `redirectTo` param.
+ See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
+ - After the user has been redirected successfully, prompt them for a new password and call `updateUser()`:
```js
const { data, error } = await supabase.auth.update({
password: new_password,
@@ -516,7 +582,8 @@ functions:
code: |
```js
const { data, error } = await supabase.auth.api.resetPasswordForEmail(
- 'user@email.com'
+ email,
+ { redirectTo: 'https://example.com/update-password' }
)
```
- id: reset-password-react
@@ -529,7 +596,8 @@ functions:
* This email contains a link which sends the user back to your application.
*/
const { data, error } = await supabase.auth.api.resetPasswordForEmail(
- 'user@email.com'
+ email,
+ { redirectTo: 'https://example.com/update-password' }
)
/**
diff --git a/spec/supabase_js_v2.yml b/spec/supabase_js_v2.yml
index 8899420a2ce..309fe559544 100644
--- a/spec/supabase_js_v2.yml
+++ b/spec/supabase_js_v2.yml
@@ -215,7 +215,8 @@ functions:
- If the user doesn't exist, `signInWithOtp()` will signup the user instead. To restrict this behaviour, you can set `shouldCreateUser` in `SignInWithPasswordlessCredentials.options` to `false`.
- 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`](/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.
examples:
- id: sign-in-with-email
name: Sign in with email
@@ -260,8 +261,8 @@ functions:
name: Sign in using a third-party provider with redirect
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/settings).
+ - When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/reference/auth/config#site_url). It does not redirect the user immediately after invoking this method.
+ - See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
code: |
```js
const { data, error } = await supabase.auth.signInWithOAuth({
@@ -734,7 +735,9 @@ functions:
- A `SIGNED_IN` and `PASSWORD_RECOVERY` event will be emitted when the password recovery link is clicked.
You can use [`onAuthStateChange()`](/docs/reference/javascript/auth-onauthstatechange) to listen and invoke a callback function on these events.
- When the user clicks the reset link in the email they are redirected back to your application.
- Prompt the user for a new password and call `updateUser()`:
+ You can configure the URL that the user is redirected to with the `redirectTo` parameter.
+ See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project.
+ - After the user has been redirected successfully, prompt them for a new password and call `updateUser()`:
```js
const { data, error } = await supabase.auth.updateUser({
password: new_password
@@ -965,6 +968,17 @@ functions:
name: Query foreign tables
description: |
If your database has foreign key relationships, you can query related tables too.
+ code: |
+ ```js
+ const { data, error } = await supabase
+ .from('countries')
+ .select(`
+ name,
+ cities (
+ name
+ )
+ `)
+ ```
- id: query-foreign-tables-through-a-join-table
name: Query foreign tables through a join table
code: |
@@ -3690,7 +3704,7 @@ functions:
const { data, error } = await supabase
.from('countries')
.select()
- .like('name', '%alba%')
+ .ilike('name', '%alba%')
```
data:
sql: |
@@ -3769,13 +3783,13 @@ functions:
$ref: '@supabase/postgrest-js.PostgrestFilterBuilder.is'
examples:
- id: checking-nullness
- name: Checking nullness
+ name: Checking for nullness, true or false
code: |
```ts
const { data, error } = await supabase
.from('countries')
.select()
- .eq('name', null)
+ .is('name', null)
```
data:
sql: |
diff --git a/studio/README.md b/studio/README.md
index 7dd002c04c3..2c4fda0ba60 100644
--- a/studio/README.md
+++ b/studio/README.md
@@ -19,7 +19,7 @@ As such, the features exposed on Studio for existing deployments are limited to
## Managing Project Settings
-Project settings are managed outside of the Dashboard. If you use docker-compose, you should manage the settings in your docker-compose file. If you're deploying Supabase to your own cloud, you should store your secrets and env vars in a vault or secrets manager.
+Project settings are managed outside of the Dashboard. If you use docker compose, you should manage the settings in your docker-compose file. If you're deploying Supabase to your own cloud, you should store your secrets and env vars in a vault or secrets manager.
## How to contribute?
@@ -38,6 +38,7 @@ Project settings are managed outside of the Dashboard. If you use docker-compose
# in /studio
npm i # install dependencies
+npm run dev:secrets:pull # Supabase internal use: if you are working on the platform version of the Studio
npm run dev # start dev server
npm run test # run tests
npm run -- --watch # run tests in watch mode
@@ -50,7 +51,7 @@ Follow the [self-hosting guide](https://supabase.com/docs/guides/hosting/docker)
```
cd ..
cd docker
-docker-compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up
+docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up
```
Once you've got that set up, update `.env` in the studio folder with the corresponding values.
diff --git a/studio/components/interfaces/Auth/Auth.types.ts b/studio/components/interfaces/Auth/Auth.types.ts
index 9837f672b7e..aebf5e91076 100644
--- a/studio/components/interfaces/Auth/Auth.types.ts
+++ b/studio/components/interfaces/Auth/Auth.types.ts
@@ -111,6 +111,7 @@ export interface AuthConfig {
OPERATOR_TOKEN: string | null
PASSWORD_MIN_LENGTH: number
RATE_LIMIT_EMAIL_SENT: number
+ RATE_LIMIT_SMS_SENT: number
RATE_LIMIT_HEADER: string | null
RATE_LIMIT_TOKEN_REFRESH: number
RATE_LIMIT_VERIFY: number
diff --git a/studio/components/interfaces/Billing/AddNewPaymentMethodModal/AddNewPaymentMethodModal.tsx b/studio/components/interfaces/Billing/AddNewPaymentMethodModal/AddNewPaymentMethodModal.tsx
index 8e8e7e865b6..f8c52edd2eb 100644
--- a/studio/components/interfaces/Billing/AddNewPaymentMethodModal/AddNewPaymentMethodModal.tsx
+++ b/studio/components/interfaces/Billing/AddNewPaymentMethodModal/AddNewPaymentMethodModal.tsx
@@ -1,9 +1,11 @@
-import { FC, useEffect, useState } from 'react'
-import { IconLoader, Modal } from 'ui'
+import HCaptcha from '@hcaptcha/react-hcaptcha'
import { loadStripe } from '@stripe/stripe-js'
import { Elements } from '@stripe/react-stripe-js'
+import { FC, useCallback, useEffect, useState } from 'react'
+import { Modal } from 'ui'
import { useStore } from 'hooks'
+import { useIsHCaptchaLoaded } from 'stores/hcaptcha-loaded-store'
import { post } from 'lib/common/fetch'
import { API_URL, STRIPE_PUBLIC_KEY } from 'lib/constants'
import AddPaymentMethodForm from './AddPaymentMethodForm'
@@ -20,16 +22,49 @@ const AddNewPaymentMethodModal: FC = ({ visible, returnUrl, onCancel }) =
const { ui } = useStore()
const [intent, setIntent] = useState()
- useEffect(() => {
- if (visible) {
- setupIntent()
- }
- }, [visible])
+ const captchaLoaded = useIsHCaptchaLoaded()
- const setupIntent = async () => {
+ const [captchaToken, setCaptchaToken] = useState(null)
+ const [captchaRef, setCaptchaRef] = useState(null)
+
+ const captchaRefCallback = useCallback((node) => {
+ setCaptchaRef(node)
+ }, [])
+
+ useEffect(() => {
+ const loadPaymentForm = async () => {
+ if (visible && captchaRef && captchaLoaded) {
+ let token = captchaToken
+
+ try {
+ if (!token) {
+ const captchaResponse = await captchaRef.execute({ async: true })
+ token = captchaResponse?.response ?? null
+ }
+ } catch (error) {
+ return
+ }
+
+ await setupIntent(token ?? undefined)
+ resetCaptcha()
+ }
+ }
+
+ loadPaymentForm()
+ }, [visible, captchaRef, captchaLoaded])
+
+ const resetCaptcha = () => {
+ setCaptchaToken(null)
+ captchaRef?.resetCaptcha()
+ }
+
+ const setupIntent = async (hcaptchaToken: string | undefined) => {
setIntent(undefined)
+
const orgSlug = ui.selectedOrganization?.slug ?? ''
- const intent = await post(`${API_URL}/organizations/${orgSlug}/payments/setup-intent`, {})
+ const intent = await post(`${API_URL}/organizations/${orgSlug}/payments/setup-intent`, {
+ hcaptchaToken,
+ })
if (intent.error) {
return ui.setNotification({
@@ -37,9 +72,9 @@ const AddNewPaymentMethodModal: FC = ({ visible, returnUrl, onCancel }) =
message: intent.error.message,
error: intent.error,
})
+ } else {
+ setIntent(intent)
}
-
- setIntent(intent)
}
const options = {
@@ -47,27 +82,43 @@ const AddNewPaymentMethodModal: FC = ({ visible, returnUrl, onCancel }) =
appearance: { theme: 'night', labels: 'floating' },
} as any
+ const onLocalCancel = () => {
+ setIntent(undefined)
+ return onCancel()
+ }
+
return (
-
-
- {intent !== undefined ? (
+ // We cant display the hCaptcha in the modal, as the modal auto-closes when clicking the captcha
+ // So we only show the modal if the captcha has been executed successfully (intent loaded)
+ <>
+ {
+ setCaptchaToken(token)
+ }}
+ onClose={onLocalCancel}
+ onExpire={() => {
+ setCaptchaToken(null)
+ }}
+ />
+
+
+
-
+
- ) : (
-
-
-
- )}
-
-
+
+
+ >
)
}
diff --git a/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts b/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts
index 093065f6d4c..6ecb12c6576 100644
--- a/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts
+++ b/studio/components/interfaces/Billing/AddOns/AddOns.utils.ts
@@ -33,7 +33,7 @@ export const formatComputeSizes = (addons: SubscriptionAddon[]) => {
metadata: {
default_price_id: undefined,
supabase_prod_id: 'addon_instance_micro',
- features: '2 CPUs • 1GB memory • 2,085Mbps Disk IO',
+ features: '2-core ARM (shared) • 1GB memory • 2,085Mbps Disk IO',
},
prices: [
{
diff --git a/studio/components/interfaces/Billing/AddOns/ComputeSizeSelection.tsx b/studio/components/interfaces/Billing/AddOns/ComputeSizeSelection.tsx
index 1b5262cc216..080b518db15 100644
--- a/studio/components/interfaces/Billing/AddOns/ComputeSizeSelection.tsx
+++ b/studio/components/interfaces/Billing/AddOns/ComputeSizeSelection.tsx
@@ -1,6 +1,6 @@
import { FC } from 'react'
import Link from 'next/link'
-import { Badge, Button, Radio } from 'ui'
+import { Badge, Button, IconExternalLink, Radio } from 'ui'
import { useFlag, useParams } from 'hooks'
import { getProductPrice } from '../Billing.utils'
@@ -25,14 +25,23 @@ const ComputeSizeSelection: FC = ({
return (
-
-
-
Database add-ons
- Optional
+
+
+
+
Compute add-ons
+ Optional
+
+
+ Choose the database instance size that best fits your needs
+
-
- Choose the database instance size that best fits your needs
-