diff --git a/apps/docs/components/Navigation/Navigation.constants.ts b/apps/docs/components/Navigation/Navigation.constants.ts
index 47c2bee3335..5bfb8aa40df 100644
--- a/apps/docs/components/Navigation/Navigation.constants.ts
+++ b/apps/docs/components/Navigation/Navigation.constants.ts
@@ -226,8 +226,9 @@ export const menuItems: NavMenu = {
label: 'Edge Functions',
items: [
{ name: 'Overview', url: '/guides/functions', items: [] },
+ { name: 'Quickstart', url: '/guides/functions/quickstart', items: [] },
{ name: 'Auth', url: '/guides/functions/auth', items: [] },
- { name: 'Examples', url: '/guides/functions/examples', items: [] },
+ { name: 'Best Practices', url: '/guides/functions/best-practices', items: [] },
{ name: 'CI/CD Workflow', url: '/guides/functions/cicd-workflow', items: [] },
],
},
@@ -313,6 +314,7 @@ export const menuItems: NavMenu = {
{ name: 'Sequin', url: '/guides/integrations/sequin', items: [] },
{ name: 'Snaplet', url: '/guides/integrations/snaplet', items: [] },
{ name: 'Vercel', url: '/guides/integrations/vercel', items: [] },
+ { name: 'Zuplo', url: '/guides/integrations/zuplo', items: [] },
],
},
{
diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs
index 151a73426e6..6e476e18228 100644
--- a/apps/docs/next.config.mjs
+++ b/apps/docs/next.config.mjs
@@ -28,7 +28,7 @@ const nextConfig = {
async headers() {
return [
{
- source: '/(.*)',
+ source: '/:path*',
headers: [
{
key: 'Strict-Transport-Security',
diff --git a/apps/docs/pages/guides/auth.mdx b/apps/docs/pages/guides/auth.mdx
index 71986837e96..2d9ae24727d 100644
--- a/apps/docs/pages/guides/auth.mdx
+++ b/apps/docs/pages/guides/auth.mdx
@@ -71,7 +71,7 @@ For deployments with Netlify, set the `SITE_URL` to your official site URL. Add
For deployments with Vercel, set the `SITE_URL` to your official site URL. Add the following additional redirect URLs for local development and deployment previews:
- `http://localhost:3000/**`
-- `https://**.vercel.app/**`
+- `https://*-username.vercel.app/**`
Vercel provides an environment variable for the URL of the deployment called `NEXT_PUBLIC_VERCEL_URL`. See the [Vercel docs](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) for more details. You can use this variable to dynamically redirect depending on the environment:
diff --git a/apps/docs/pages/guides/functions.mdx b/apps/docs/pages/guides/functions.mdx
index dc9d680ec67..9822760ab50 100644
--- a/apps/docs/pages/guides/functions.mdx
+++ b/apps/docs/pages/guides/functions.mdx
@@ -1,280 +1,31 @@
import Layout from '~/layouts/DefaultGuideLayout'
+import {Button} from 'ui'
export const meta = {
id: 'functions',
title: 'Edge Functions',
description: 'Globally distributed TypeScript functions.',
sidebar_label: 'Overview',
+ hide_table_of_contents: true,
}
-Edge Functions are server-side TypeScript functions, distributed globally at the edge—close to your users. They can be used for listening to webhooks or integrating your Supabase project with third-parties [like Stripe](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/stripe-webhooks).
-
-Edge Functions are developed using [Deno](https://deno.com), which offers a few benefits to you as a developer:
+Edge Functions are server-side TypeScript functions, distributed globally at the edge—close to your users. They can be used for listening to webhooks or integrating your Supabase project with third-parties [like Stripe](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/stripe-webhooks). Edge Functions are developed using [Deno](https://deno.com), which offers a few benefits to you as a developer:
- It is open source.
- It is portable. Supabase Edge Functions run locally, and on any other Deno-compatible platform (including self-hosted infrastructure).
- It is TypeScript first and supports WASM.
- Edge Functions are globally distributed for low-latency.
-## Quickstart
+
-Learn how to develop Edge Functions in less than 7 minutes:
+## Examples
-
-
+Check out the [Edge Function Examples](https://github.com/supabase/supabase/tree/master/examples/edge-functions) in our GitHub repository.
+
+
+
-## Prerequisites
-
-Follow the steps to prepare your Supabase project on your local machine.
-
-- Install the Supabase CLI. [Docs](/docs/guides/cli).
-- Login to the CLI using the command: `supabase login`. [Docs](/docs/reference/cli/usage#supabase-login).
-- Initialize Supabase inside your project using the command: `supabase init`. [Docs](/docs/guides/cli/local-development#getting-started).
-- Link to your Remote Project using the command `supabase link --project-ref your-project-ref`. [Docs](/docs/reference/cli/usage#supabase-link).
-- Optional: Setup your environment: Follow [this setup guide](https://deno.land/manual/getting_started/setup_your_environment) to integrate the Deno language server with your editor.
-
-## Getting Started
-
-Let's build an Edge Function locally, then deploy it to the Supabase Platform.
-
-### Creating a function
-
-Let's create a new Edge Function called `hello-world` inside your project:
-
-```bash
-supabase functions new hello-world
-```
-
-This creates a function stub in your `supabase` folder at `./functions/hello-world/index.ts`.
-
-### Deploy to production
-
-```bash
-supabase functions deploy hello-world
-```
-
-This command bundles your Edge Function from `./functions/hello-world/index.ts` and deploys it to the Supabase platform.
-The command outputs a URL to the Supabase Dashboard which you can open to find view more details. Let's open the link to find the execution command.
-
-
-
-By default, Edge Functions require a valid JWT in the authorization header. This header is automatically set when invoking your function via a Supabase client library.
-
-If you want to use Edge Functions to handle webhooks (e.g. [Stripe payment webhooks](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/stripe-webhooks) etc.), you need to pass the `--no-verify-jwt` flag when deploying your function.
-
-
-
-### Executing Remote Functions
-
-You can execute Edge Functions using curl. Copy the curl command from the Dashboard. It should look like this:
-
-```bash
-curl --request POST 'https://.functions.supabase.co/hello-world' \
- --header 'Authorization: Bearer ANON_KEY' \
- --header 'Content-Type: application/json' \
- --data '{ "name":"Functions" }'
-```
-
-If you receive an error `Invalid JWT`, find the `ANON_KEY` of your project in the Dashboard under `Settings > API`.
-
-After invoking your Edge Function you should see the response `{ "message":"Hello Functions!" }`.
-
-## Debugging your Functions
-
-You can debug your deployed Edge Functions using the "Functions" section of the Dashboard. There are two types debugging tools available:
-
-- Invocations: shows the Request and Response for each execution.
-- Logs: shows any platform events, including deployments and errors.
-
-
-
-## Developing locally
-
-You can run your Edge Function locally using [`supabase functions serve`](/docs/reference/cli/usage#supabase-functions-serve):
-
-```bash
-supabase start # start the supabase stack
-supabase functions serve hello-world # start the Function watcher
-```
-
-The `functions serve` command has hot-reloading capabilities. It will watch for any changes to your files and restart the Deno server.
-
-### Invoking Functions locally
-
-While serving your local Function, you can execute it using curl:
-
-```bash
-curl --request POST 'http://localhost:54321/functions/v1/hello-world' \
- --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \
- --header 'Content-Type: application/json' \
- --data '{ "name":"Functions" }'
-```
-
-You should see the response `{ "message":"Hello Functions!" }`.
-
-
-Implementation details
-
-- All Edge Functions are `POST` requests.
-- The `Authorization` header is required. You can use either the `ANON` key, the `SERVICE_ROLE` key, or a logged-in user's JWT.
-- The Function is proxied through the local API (`http://localhost:54321`)
-
-
-
-
-If you execute Function with a different payload the response will change.
-Modify the `--data '{"name":"Functions"}'` line to `--data '{"name":"World"}'` and try invoking the command again!
-
-## Secrets and Environment Variables
-
-It's common that you will need to use sensitive information or environment-specific variables inside your Edge Functions. You can access these using Deno's built-in handler
-
-```js
-Deno.env.get(MY_SECRET_NAME)
-```
-
-### Default secrets
-
-By default, Edge Functions have access to these secrets:
-
-- `SUPABASE_URL`: The API gateway for your Supabase project.
-- `SUPABASE_ANON_KEY`: The `anon` key for your Supabase API. This is safe to use in a browser when you have [Row Level Security](/docs/guides/auth/row-level-security) enabled.
-- `SUPABASE_SERVICE_ROLE_KEY`: The `service_role` key for your Supabase API. This is safe to use in Edge Functions, but it should NEVER be used in a browser. This key will bypass [Row Level Security](/docs/guides/auth/row-level-security).
-- `SUPABASE_DB_URL`: The URL for your [PostgreSQL database](/docs/guides/database). You can use this to connect directly to your database.
-
-### Local secrets
-
-Let's create a local file for storing our secrets, and inside it we can store a secret `MY_NAME`:
-
-```jsx
-echo "MY_NAME=Yoda" >> ./supabase/.env.local
-```
-
-This creates a new file `./supabase/.env.local` for storing your local development secrets.
-
-
-
-Never check your .env files into Git!
-
-
-
-Now let's access this environment variable `MY_NAME` inside our Function. Anywhere in your function, add this line:
-
-```jsx
-console.log(Deno.env.get('MY_NAME'))
-```
-
-Now we can invoke our function locally, by serving it with our new `.env.local` file:
-
-```bash
-supabase functions serve hello-world --env-file ./supabase/.env.local
-```
-
-When the function starts you should see the name “Yoda” output to the terminal.
-
-### Production secrets
-
-Let's create a `.env` for production. In this case we'll just use the same as our local secrets:
-
-```bash
-cp ./supabase/.env.local ./supabase/.env
-```
-
-This creates a new file `./supabase/.env` for storing your production secrets.
-
-
-
-Never check your `.env` files into Git!
-
-
-
-Let's push all the secrets from the `.env` file to our remote project using [`supabase secrets set`](/docs/reference/cli/usage#supabase-secrets-set):
-
-```bash
-supabase secrets set --env-file ./supabase/.env
-
-# You can also set secrets individually using:
-supabase secrets set MY_NAME=Chewbacca
-```
-
-You don't need to re-deploy after setting your secrets.
-
-To see all the secrets which you have set remotely, use [`supabase secrets list`](/docs/reference/cli/usage#supabase-secrets-list):
-
-```bash
-supabase secrets list
-```
-
-## Suggestions
-
-### Database Functions vs Edge Functions
-
-For data-intensive operations we recommend using [Database Functions](/docs/guides/database/functions), which are executed within your database
-and can be called remotely using the [REST and GraphQL API](/docs/guides/api).
-
-For use-cases which require low-latency we recommend [Edge Functions](/docs/guides/functions), which are globally-distributed and can be written in TypeScript.
-
-### Organizing your Edge Functions
-
-We recommend developing “fat functions”. This means that you should develop few large functions, rather than many small functions. One common pattern when developing Functions is that you need to share code between two or more Functions. To do this, you can store any shared code in a folder prefixed with an underscore (`_`). We recommend this folder structure:
-
-```bash
-└── supabase
- ├── functions
- │ ├── _shared
- │ | ├── supabaseAdmin.ts # Supabase client with SERVICE_ROLE key
- │ │ └── supabaseClient.ts # Supabase client with ANON key
- │ ├── function-one # use hyphens to name functions
- │ │ └── index.ts
- │ └── function-two
- │ └── index.ts
- ├── migrations
- └── config.toml
-```
-
-### Naming Edge Functions
-
-We recommend using hyphens to name functions because hyphens are the most URL-friendly of all the naming conventions (snake_case, camelCase, PascalCase).
-
-### CORS (Cross-Origin Resource Sharing)
-
-We recommend adding a check to handle [CORS Preflight](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) requests in your edge function to be able to invoke the function from browsers.
-
-See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/browser-with-cors/index.ts).
-
-```ts
-export const corsHeaders = {
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey',
-}
-
-serve(async (req) => {
- if (req.method === 'OPTIONS') {
- return new Response('ok', { headers: corsHeaders })
- }
- ...
-})
-```
-
-### Using HTTP Methods
-
-Edge Functions supports `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, and `OPTIONS`. A function can be designed to perform different actions based on a request's HTTP method. See the [example on building a RESTful service](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/restful-tasks) to learn how to handle different HTTP methods in your function.
-
-## Limitations
-
-- Deno Deploy limitations
- - Deno does not support outgoing connections to ports `25`, `465`, and `587`.
- - Cannot write to File System
-- Edge Functions
- - Local development - only one function at a time
- - Serving of HTML content is not supported (`GET` requests that return `text/html` will be rewritten to `text/plain`).
-
export const Page = ({ children }) =>
export default Page
diff --git a/apps/docs/pages/guides/functions/best-practices.mdx b/apps/docs/pages/guides/functions/best-practices.mdx
new file mode 100644
index 00000000000..b670cd73a05
--- /dev/null
+++ b/apps/docs/pages/guides/functions/best-practices.mdx
@@ -0,0 +1,64 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'functions-best-practices',
+ title: 'Edge Functions Best Practices',
+ description: 'Globally distributed TypeScript functions.',
+}
+
+## Database Functions vs Edge Functions
+
+For data-intensive operations we recommend using [Database Functions](/docs/guides/database/functions), which are executed within your database
+and can be called remotely using the [REST and GraphQL API](/docs/guides/api).
+
+For use-cases which require low-latency we recommend [Edge Functions](/docs/guides/functions), which are globally-distributed and can be written in TypeScript.
+
+## Organizing your Edge Functions
+
+We recommend developing “fat functions”. This means that you should develop few large functions, rather than many small functions. One common pattern when developing Functions is that you need to share code between two or more Functions. To do this, you can store any shared code in a folder prefixed with an underscore (`_`). We recommend this folder structure:
+
+```bash
+└── supabase
+ ├── functions
+ │ ├── _shared
+ │ | ├── supabaseAdmin.ts # Supabase client with SERVICE_ROLE key
+ │ │ └── supabaseClient.ts # Supabase client with ANON key
+ │ ├── function-one # use hyphens to name functions
+ │ │ └── index.ts
+ │ └── function-two
+ │ └── index.ts
+ ├── migrations
+ └── config.toml
+```
+
+## Naming Edge Functions
+
+We recommend using hyphens to name functions because hyphens are the most URL-friendly of all the naming conventions (snake_case, camelCase, PascalCase).
+
+## CORS (Cross-Origin Resource Sharing)
+
+We recommend adding a check to handle [CORS Preflight](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) requests in your edge function to be able to invoke the function from browsers.
+
+See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/browser-with-cors/index.ts).
+
+```ts
+export const corsHeaders = {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey',
+}
+
+serve(async (req) => {
+ if (req.method === 'OPTIONS') {
+ return new Response('ok', { headers: corsHeaders })
+ }
+ ...
+})
+```
+
+## Using HTTP Methods
+
+Edge Functions supports `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, and `OPTIONS`. A function can be designed to perform different actions based on a request's HTTP method. See the [example on building a RESTful service](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/restful-tasks) to learn how to handle different HTTP methods in your function.
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/functions/quickstart.mdx b/apps/docs/pages/guides/functions/quickstart.mdx
new file mode 100644
index 00000000000..963305181a3
--- /dev/null
+++ b/apps/docs/pages/guides/functions/quickstart.mdx
@@ -0,0 +1,210 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'functions-quickstart',
+ title: 'Edge Functions Quickstart',
+ description: 'Globally distributed TypeScript functions.',
+ sidebar_label: 'Quickstart',
+}
+
+Learn how to build an Edge Function locally and deploy it to the Supabase Platform in less than 7 minutes.
+
+
+
+
+
+## Prerequisites
+
+Follow the steps to prepare your Supabase project on your local machine.
+
+- Install the Supabase CLI. [Docs](/docs/guides/cli).
+- Login to the CLI using the command: `supabase login`. [Docs](/docs/reference/cli/usage#supabase-login).
+- Initialize Supabase inside your project using the command: `supabase init`. [Docs](/docs/guides/cli/local-development#getting-started).
+- Link to your Remote Project using the command `supabase link --project-ref your-project-ref`. [Docs](/docs/reference/cli/usage#supabase-link).
+- Optional: Setup your environment: Follow [this setup guide](https://deno.land/manual/getting_started/setup_your_environment) to integrate the Deno language server with your editor.
+
+## Create a function
+
+Let's create a new Edge Function called `hello-world` inside your project:
+
+```bash
+supabase functions new hello-world
+```
+
+This creates a function stub in your `supabase` folder at `./functions/hello-world/index.ts`.
+
+## Deploy to production
+
+```bash
+supabase functions deploy hello-world
+```
+
+This command bundles your Edge Function from `./functions/hello-world/index.ts` and deploys it to the Supabase platform.
+The command outputs a URL to the Supabase Dashboard which you can open to find view more details. Let's open the link to find the execution command.
+
+
+
+By default, Edge Functions require a valid JWT in the authorization header. This header is automatically set when invoking your function via a Supabase client library.
+
+If you want to use Edge Functions to handle webhooks (e.g. [Stripe payment webhooks](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/stripe-webhooks) etc.), you need to pass the `--no-verify-jwt` flag when deploying your function.
+
+
+
+## Execute remote functions
+
+You can execute Edge Functions using curl. Copy the curl command from the Dashboard. It should look like this:
+
+```bash
+curl --request POST 'https://.functions.supabase.co/hello-world' \
+ --header 'Authorization: Bearer ANON_KEY' \
+ --header 'Content-Type: application/json' \
+ --data '{ "name":"Functions" }'
+```
+
+If you receive an error `Invalid JWT`, find the `ANON_KEY` of your project in the Dashboard under `Settings > API`.
+
+After invoking your Edge Function you should see the response `{ "message":"Hello Functions!" }`.
+
+## Debug functions
+
+You can debug your deployed Edge Functions using the "Functions" section of the Dashboard. There are two types debugging tools available:
+
+- Invocations: shows the Request and Response for each execution.
+- Logs: shows any platform events, including deployments and errors.
+
+
+
+## Develop locally
+
+You can run your Edge Function locally using [`supabase functions serve`](/docs/reference/cli/usage#supabase-functions-serve):
+
+```bash
+supabase start # start the supabase stack
+supabase functions serve hello-world # start the Function watcher
+```
+
+The `functions serve` command has hot-reloading capabilities. It will watch for any changes to your files and restart the Deno server.
+
+### Invoke functions locally
+
+While serving your local Function, you can execute it using curl:
+
+```bash
+curl --request POST 'http://localhost:54321/functions/v1/hello-world' \
+ --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \
+ --header 'Content-Type: application/json' \
+ --data '{ "name":"Functions" }'
+```
+
+You should see the response `{ "message":"Hello Functions!" }`.
+
+
+Implementation details
+
+- All Edge Functions are `POST` requests.
+- The `Authorization` header is required. You can use either the `ANON` key, the `SERVICE_ROLE` key, or a logged-in user's JWT.
+- The Function is proxied through the local API (`http://localhost:54321`)
+
+
+
+
+If you execute Function with a different payload the response will change.
+Modify the `--data '{"name":"Functions"}'` line to `--data '{"name":"World"}'` and try invoking the command again!
+
+## Secrets and Environment Variables
+
+It's common that you will need to use sensitive information or environment-specific variables inside your Edge Functions. You can access these using Deno's built-in handler
+
+```js
+Deno.env.get(MY_SECRET_NAME)
+```
+
+### Default secrets
+
+By default, Edge Functions have access to these secrets:
+
+- `SUPABASE_URL`: The API gateway for your Supabase project.
+- `SUPABASE_ANON_KEY`: The `anon` key for your Supabase API. This is safe to use in a browser when you have [Row Level Security](/docs/guides/auth/row-level-security) enabled.
+- `SUPABASE_SERVICE_ROLE_KEY`: The `service_role` key for your Supabase API. This is safe to use in Edge Functions, but it should NEVER be used in a browser. This key will bypass [Row Level Security](/docs/guides/auth/row-level-security).
+- `SUPABASE_DB_URL`: The URL for your [PostgreSQL database](/docs/guides/database). You can use this to connect directly to your database.
+
+### Local secrets
+
+Let's create a local file for storing our secrets, and inside it we can store a secret `MY_NAME`:
+
+```jsx
+echo "MY_NAME=Yoda" >> ./supabase/.env.local
+```
+
+This creates a new file `./supabase/.env.local` for storing your local development secrets.
+
+
+
+Never check your .env files into Git!
+
+
+
+Now let's access this environment variable `MY_NAME` inside our Function. Anywhere in your function, add this line:
+
+```jsx
+console.log(Deno.env.get('MY_NAME'))
+```
+
+Now we can invoke our function locally, by serving it with our new `.env.local` file:
+
+```bash
+supabase functions serve hello-world --env-file ./supabase/.env.local
+```
+
+When the function starts you should see the name “Yoda” output to the terminal.
+
+### Production secrets
+
+Let's create a `.env` for production. In this case we'll just use the same as our local secrets:
+
+```bash
+cp ./supabase/.env.local ./supabase/.env
+```
+
+This creates a new file `./supabase/.env` for storing your production secrets.
+
+
+
+Never check your `.env` files into Git!
+
+
+
+Let's push all the secrets from the `.env` file to our remote project using [`supabase secrets set`](/docs/reference/cli/usage#supabase-secrets-set):
+
+```bash
+supabase secrets set --env-file ./supabase/.env
+
+# You can also set secrets individually using:
+supabase secrets set MY_NAME=Chewbacca
+```
+
+You don't need to re-deploy after setting your secrets.
+
+To see all the secrets which you have set remotely, use [`supabase secrets list`](/docs/reference/cli/usage#supabase-secrets-list):
+
+```bash
+supabase secrets list
+```
+
+## Limitations
+
+- Deno Deploy limitations
+ - Deno does not support outgoing connections to ports `25`, `465`, and `587`.
+ - Cannot write to File System
+- Edge Functions
+ - Local development - only one function at a time
+ - Serving of HTML content is not supported (`GET` requests that return `text/html` will be rewritten to `text/plain`).
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/hosting/overview.mdx b/apps/docs/pages/guides/hosting/overview.mdx
index 27c77f0421a..3d6fa1ff753 100644
--- a/apps/docs/pages/guides/hosting/overview.mdx
+++ b/apps/docs/pages/guides/hosting/overview.mdx
@@ -39,7 +39,7 @@ Each system has a number of configuration options which can be found in the rele
- [Realtime](https://github.com/supabase/realtime#server)
- [GoTrue](https://github.com/supabase/gotrue)
- [Storage](https://github.com/supabase/storage-api)
-- [Kong](https://docs.konghq.com/install/docker/)
+- [Kong](https://docs.konghq.com/gateway/latest/install/docker/)
## Managing your database
diff --git a/apps/docs/pages/guides/integrations/zuplo.mdx b/apps/docs/pages/guides/integrations/zuplo.mdx
new file mode 100644
index 00000000000..c4eb7cc98e4
--- /dev/null
+++ b/apps/docs/pages/guides/integrations/zuplo.mdx
@@ -0,0 +1,241 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'zuplo',
+ title: 'Zuplo',
+ description: 'Building a public API backed by Supabase.',
+}
+
+[Zuplo](https://zuplo.com) is a fully-managed API gateway that offers the easiest way to securely and safely share your API. In this guide we look at how you can combine Zuplo and Supabase to create a public API with rate-limiting, a self-serve developer portal, and API-key authentication. There is an [accompanying video for this article](https://www.youtube.com/watch?v=GJSkbxMnWxE).
+
+
+
+In this example we're going to work with a simple table that allows people to read and write entries to a Supabase table that contains some reviews of skis. Because this is an API for developers, we have to assume that they may be calling it from another backend service and can't login as a user using the standard Supabase method. In this scenario, API keys are often a better choice - see [Wait, you're not using API keys?](https://zuplo.com/blog/2022/05/03/you-should-be-using-api-keys/).
+
+We'll allow people, with a valid API key, to read data from the ski results table and to create new records. Hopefully it's obvious that there are many ways that you can extend this example to add more behavior like roles based access, with custom policies, custom handlers and more.
+
+## Setting up Supabase
+
+If you haven't already, create a new project in Supabase and create a table called ski-reviews with the following columns:
+
+- id (int8)
+- created_at (timestamptz)
+- make (varchar)
+- model (varchar)
+- year (int8)
+- rating (int2)
+- author (varchar)
+
+Manually enter a couple of rows of data, so that we have something to read from the DB.
+
+## The `Get all` reviews route in Zuplo
+
+Login to Zuplo at [portal.zuplo.com](https://portal.zuplo.com] and create a new project in Zuplo - I went with `supabase-ski-reviews`.
+
+Select the **File** tab and choose **Routes**. Add your first route with the following settings:
+
+- method: `GET`
+- path: `/reviews`
+- summary: `Get all reviews`
+- version: `v1`
+- CORS: `Anything goes`
+
+And in the request handler section, paste the `READ ALL ROWS` URL of your Supabase backend (you can get to this in the **API docs** section of Supabase)
+
+- URL Rewrite: `https://YOUR_SUPABASE_URL.supabase.co/rest/v1/ski-reviews?select=*`
+- Forward Search: `unchecked`
+
+In order to call the Supabase backend I need to add some authentication headers to the outgoing request.
+
+Expand the **Policies** section of your route. Click **Add policy** on the **Request** pipeline.
+
+We don't want to forward any headers that the client sends us to Supabase, so find the **Clear Headers Policy** and add that to your inbound pipeline. Note, that we will allow the `content-type` header to flow through, so this should be your policy config.
+
+```json
+{
+ "export": "ClearHeadersInboundPolicy",
+ "module": "$import(@zuplo/runtime)",
+ "options": {
+ "exclude": ["content-type"]
+ }
+}
+```
+
+Next, we need to add the credentials to the outgoing request. We'll need to get the JWT token from supabase - you'll find it in **Settings** > **API** as shown below:
+
+
+
+Once you've got your service_role JWT, click **Add Policy** again on the **Request** pipeline and choose the **Add/Set Headers Policy** and configure it as follows:
+
+```json
+{
+ "export": "SetHeadersInboundPolicy",
+ "module": "$import(@zuplo/runtime)",
+ "options": {
+ "headers": [
+ {
+ "name": "apikey",
+ "value": "$env(SUPABASE_API_KEY)",
+ "overwrite": true
+ },
+ {
+ "name": "authorization",
+ "value": "$env(SUPABASE_AUTHZ_HEADER)",
+ "overwrite": true
+ }
+ ]
+ }
+}
+```
+
+Save your changes.
+
+Next, create two secret [environment variables](https://zuplo.com/docs/deployments/environment-variables) as follows:
+
+- SUPABASE_API_KEY: `"YOUR_SUPABASE_SECRET_ROLE_JWT"`
+- SUPABASE_AUTHZ_HEADER: `"Bearer YOUR_SUPABASE_SECRET_ROLE_JWT"`
+
+Obviously, in both instances replace `YOUR_SUPABASE_SECRET_ROLE_JWT` with your service_role JWT from Supabase.
+
+You are now ready to invoke your API gateway and see data flow through from your Supabase backend!
+
+Click on the **open in browser** button shown below and you should see the JSON, flowing from Supabase in your browser 👏.
+
+
+
+## Adding authentication
+
+At this point, that route is wide open to the world so we need to secure it. We'll do this using API keys. You can follow this guide [Add API key Authentication](https://zuplo.com/docs/quickstarts/add-api-key-auth). Be sure to drag the API Key authentication policy to the very top of your **Request** pipeline. Come back here when you're done.
+
+Welcome back! You've now learned how to secure your API with API-Keys.
+
+## Adding a Create route
+
+Next we'll add a route that allows somebody to create a review. Add another route with the following settings
+
+- method: `POST`
+- path: `/reviews`
+- summary: `Create a new review`
+- version: `v1`
+- CORS: `Anything goes`
+
+And the request handler as follows:
+
+- URL Rewrite: `https://YOUR_SUPABASE_URL.supabase.co/rest/v1/ski-reviews`
+- Forward Search: `unchecked`
+
+Expand the policies section and add the same policies (note you can reuse policies by picking from the existing policies at the top of the library)
+
+
+
+- api-key-auth-inbound
+- clear-headers-inbound
+- set-headers-inbound
+
+Now your **create** route is secured and will automatically set the right headers before calling Supabase. That was easy.
+
+You can test this out by using the **API Test Console** to invoke your new endpoint. Go to the **API Test Console** and create a new test called `create-review.json`.
+
+- Method: `POST`
+- Path: `/v1/reviews`
+- Headers:
+ - `content-type`: `application/json`
+ - `authorization`: `Bearer YOUR_ZUPLO_API_KEY`
+- Body:
+
+```json
+{
+ "make": "Rossignol",
+ "model": "Soul HD7",
+ "rating": 5,
+ "year": 2019
+}
+```
+
+
+
+If you invoke your API by clicking `Test` you should see that you get a **201 Created** - congratulations!
+
+## Add validation to your post
+
+To make your API more usable and more secure it is good practice to validate incoming requests. In this case we will add a JSON Schema document and use it to validate the incoming body to our POST.
+
+Create a new schema document called `new-review.json`.
+
+
+
+This example fits the ski-reviews table we described above
+
+```json
+{
+ "$id": "http://example.com/example.json",
+ "type": "object",
+ "default": {},
+ "title": "Root Schema",
+ "required": ["make", "model", "rating", "year"],
+ "additionalProperties": false,
+ "properties": {
+ "make": {
+ "type": "string",
+ "default": "",
+ "title": "The make Schema",
+ "examples": ["DPS"]
+ },
+ "model": {
+ "type": "string",
+ "default": "",
+ "title": "The model Schema",
+ "examples": ["Pagoda"]
+ },
+ "rating": {
+ "type": "integer",
+ "default": 0,
+ "title": "The rating Schema",
+ "examples": [5]
+ },
+ "year": {
+ "type": "integer",
+ "default": 0,
+ "title": "The year Schema",
+ "examples": [2018]
+ }
+ },
+ "examples": [
+ {
+ "make": "DPS",
+ "model": "Pagoda",
+ "rating": 5,
+ "year": 2018,
+ "author": "Josh"
+ }
+ ]
+}
+```
+
+Now add a new policy to **request** pipeline for your `Create new review` route. Choose the **JSON Body Validation** policy and configure it to use your newly created JSON schema document:
+
+```json
+{
+ "export": "ValidateJsonSchemaInbound",
+ "module": "$import(@zuplo/runtime)",
+ "options": {
+ "validator": "$import(./schemas/new-review.json)"
+ }
+}
+```
+
+This policy can be dragged to the first position in your pipeline.
+
+Now to test this is working, go back to your API test console and change the body of your `create-review.json` test to be invalid (add a new property for example). You should find that you get a `400 Bad Request` response.
+
+
+
+Finally, lean back and marvel at your beautiful Developer Portal that took almost zero effort to get this far, wow! Hopefully you already found the link for this when adding API key support :)
+
+
+
+Zuplo can also be used to handle Supabase JWT tokens for any API, learn more at [API Authentication with Supabase JWT Tokens](https://zuplo.com/blog/2022/11/15/api-authentication-with-supabase-jwt)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/platform/logs.mdx b/apps/docs/pages/guides/platform/logs.mdx
index 268f78240c8..995683676ed 100644
--- a/apps/docs/pages/guides/platform/logs.mdx
+++ b/apps/docs/pages/guides/platform/logs.mdx
@@ -40,31 +40,6 @@ To update the screenshots, ensure that at least one log line is selected to disp
[Postgres logs](https://app.supabase.com/project/_/logs/postgres-logs) show queries and activity for your [database](../../guides/database).
-By default, query logs are disabled for all new Supabase projects, as they can reveal metadata about the contents of your database (e.g. table and column names). If you'd like to enable query logs, you can enable and configure the [pgAudit extension](https://www.pgaudit.org/):
-
-- [Enable the pgAudit extension for your project](https://app.supabase.com/project/_/database/extensions)
-- [Reboot the DB](https://app.supabase.com/project/_/settings/general); a "Fast database reboot" is sufficient, and faster than a full project restart
-- Configure `pgaudit.log` to the [appropriate value](https://github.com/pgaudit/pgaudit/blob/master/README.md#pgauditlog)
-- You should be able to view query logs in your [log explorer](https://app.supabase.com/project/_/logs/explorer) now
-
-As an example, if you wanted to enable logs for writes and DDL statements for a single session, you could execute within the session:
-
-```sql
-set pgaudit.log = 'write, ddl';
-```
-
-To _permanently_ enable logs for all statements that relate to roles and privileges, you could execute (followed by a db reboot):
-
-```sql
-alter system set pgaudit.log to 'role';
-```
-
-To remove a system-wide setting, you can use (followed by a db reboot):
-
-```sql
-alter system reset pgaudit.log
-```
-

@@ -108,6 +83,50 @@ The Logs tab displays logs emitted during function execution.
+---
+
+## Logging Postgres Queries
+
+By default, query logs are disabled for new Supabase projects, as they can reveal metadata about the contents of your database (such as table and column names).
+
+To enable query logs:
+
+1. [Enable the pgAudit extension](https://app.supabase.com/project/_/database/extensions).
+2. [Restart your project](https://app.supabase.com/project/_/settings/general) using the **Fast database reboot** option.
+3. Configure `pgaudit.log` (see below). Perform a fast reboot if needed.
+4. View your query logs under [Logs > Postgres Logs](https://app.supabase.com/project/_/logs/postgres-logs).
+
+### Configuring `pgaudit.log`
+
+The stored value under `pgaudit.log` determines the classes of statements that are logged by [pgAudit extension](https://www.pgaudit.org/). Refer to the pgAudit documentation for the [full list of values](https://github.com/pgaudit/pgaudit/blob/master/README.md#pgauditlog).
+
+To enable logging for function calls/do blocks, writes, and DDL statements for a single session, execute the following within the session:
+
+```sql
+-- temporary single-session config update
+set pgaudit.log = 'function, write, ddl';
+```
+
+To _permanently_ set a logging configuration (beyond a single session), execute the following, then perform a fast reboot:
+
+```sql
+-- equivalent permanent config update.
+alter role postgres set pgaudit.log to 'function, write, ddl';
+```
+
+To reset system-wide settings, execute the following, then perform a fast reboot:
+
+```sql
+-- resets stored config.
+alter role postgres reset pgaudit.log
+```
+
+
+
+If any permission errors are encountered when executing `alter role postgres ...`, it is likely that your project has yet to receive the patch to the latest version of [supautils](https://github.com/supabase/supautils), which is currently being rolled out.
+
+
+
## Logs Explorer
The [Logs Explorer](https://app.supabase.com/project/_/logs-explorer) exposes logs from each part of the Supabase stack as a separate table that can be queried and joined using SQL.
diff --git a/apps/docs/pages/guides/realtime.mdx b/apps/docs/pages/guides/realtime.mdx
index f2142bc69dd..acf13a0f57b 100644
--- a/apps/docs/pages/guides/realtime.mdx
+++ b/apps/docs/pages/guides/realtime.mdx
@@ -10,7 +10,7 @@ export const meta = {
Supabase provides a globally distributed cluster of [Realtime](https://github.com/supabase/realtime) servers that enable the following functionality:
- [Broadcast](#broadcast): Send ephemeral messages from client to clients with low latency.
-- [Presence](#presence): Track and synchrononize shared state between clients.
+- [Presence](#presence): Track and synchronize shared state between clients.
- [Postgres CDC](#postgres-cdc): Listen to Postgres database changes and send them to authorized clients.
A [channel](https://hexdocs.pm/phoenix/channels.html) is the basic building block of Realtime and narrows the scope of data flow to subscribed clients. You can think of a channel as a chatroom where participants are able to see who's online and send and receive messages; similar to a Discord or Slack channel.
diff --git a/apps/docs/public/img/guides/integrations/zuplo/400-bad.png b/apps/docs/public/img/guides/integrations/zuplo/400-bad.png
new file mode 100644
index 00000000000..a1a5ec5d4e1
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/zuplo/400-bad.png differ
diff --git a/apps/docs/public/img/guides/integrations/zuplo/arch.png b/apps/docs/public/img/guides/integrations/zuplo/arch.png
new file mode 100644
index 00000000000..af425e632a2
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/zuplo/arch.png differ
diff --git a/apps/docs/public/img/guides/integrations/zuplo/dev-portal.png b/apps/docs/public/img/guides/integrations/zuplo/dev-portal.png
new file mode 100644
index 00000000000..8c4e582be93
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/zuplo/dev-portal.png differ
diff --git a/apps/docs/public/img/guides/integrations/zuplo/existing-policies.png b/apps/docs/public/img/guides/integrations/zuplo/existing-policies.png
new file mode 100644
index 00000000000..26d38ed4dac
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/zuplo/existing-policies.png differ
diff --git a/apps/docs/public/img/guides/integrations/zuplo/new-schema.png b/apps/docs/public/img/guides/integrations/zuplo/new-schema.png
new file mode 100644
index 00000000000..937857f37dd
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/zuplo/new-schema.png differ
diff --git a/apps/docs/public/img/guides/integrations/zuplo/open-in-browser.png b/apps/docs/public/img/guides/integrations/zuplo/open-in-browser.png
new file mode 100644
index 00000000000..e04e2ca4764
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/zuplo/open-in-browser.png differ
diff --git a/apps/docs/public/img/guides/integrations/zuplo/secret-role.png b/apps/docs/public/img/guides/integrations/zuplo/secret-role.png
new file mode 100644
index 00000000000..7cd456a947b
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/zuplo/secret-role.png differ
diff --git a/apps/docs/public/img/guides/integrations/zuplo/test-console.png b/apps/docs/public/img/guides/integrations/zuplo/test-console.png
new file mode 100644
index 00000000000..f51f187d07e
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/zuplo/test-console.png differ
diff --git a/apps/docs/public/sitemap.xml b/apps/docs/public/sitemap.xml
index 27044787652..6d2c085106e 100644
--- a/apps/docs/public/sitemap.xml
+++ b/apps/docs/public/sitemap.xml
@@ -78,6 +78,24 @@
0.5
+
+ https://supabase.com/docs/handbook/contributing
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/handbook/introduction
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/handbook/supasquad
+ weekly
+ 0.5
+
+
https://supabase.com/docs/guides/apiweekly
@@ -217,19 +235,31 @@
- https://supabase.com/docs/handbook/contributing
+ https://supabase.com/docs/learn/auth-deep-dive/auth-deep-dive-jwtsweekly0.5
- https://supabase.com/docs/handbook/introduction
+ https://supabase.com/docs/learn/auth-deep-dive/auth-google-oauthweekly0.5
- https://supabase.com/docs/handbook/supasquad
+ https://supabase.com/docs/learn/auth-deep-dive/auth-gotrue
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/learn/auth-deep-dive/auth-policies
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/learn/auth-deep-dive/auth-row-level-securityweekly0.5
@@ -252,6 +282,36 @@
0.5
+
+ https://supabase.com/docs/guides/functions/auth
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/guides/functions/best-practices
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/guides/functions/cicd-workflow
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/guides/functions/examples
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/guides/functions/quickstart
+ weekly
+ 0.5
+
+
https://supabase.com/docs/guides/auth/auth-appleweekly
@@ -420,42 +480,6 @@
0.5
-
- https://supabase.com/docs/guides/functions/auth
- weekly
- 0.5
-
-
-
- https://supabase.com/docs/guides/functions/cicd-workflow
- weekly
- 0.5
-
-
-
- https://supabase.com/docs/guides/functions/examples
- weekly
- 0.5
-
-
-
- https://supabase.com/docs/guides/hosting/docker
- weekly
- 0.5
-
-
-
- https://supabase.com/docs/guides/hosting/overview
- weekly
- 0.5
-
-
-
- https://supabase.com/docs/guides/hosting/platform
- weekly
- 0.5
-
-
https://supabase.com/docs/guides/database/arraysweekly
@@ -541,25 +565,19 @@
- https://supabase.com/docs/guides/migrations/firebase-auth
+ https://supabase.com/docs/guides/hosting/dockerweekly0.5
- https://supabase.com/docs/guides/migrations/firebase-storage
+ https://supabase.com/docs/guides/hosting/overviewweekly0.5
- https://supabase.com/docs/guides/migrations/firestore-data
- weekly
- 0.5
-
-
-
- https://supabase.com/docs/guides/migrations/heroku
+ https://supabase.com/docs/guides/hosting/platformweekly0.5
@@ -612,6 +630,12 @@
0.5
+
+ https://supabase.com/docs/guides/integrations/estuary
+ weekly
+ 0.5
+
+
https://supabase.com/docs/guides/integrations/feztoweekly
@@ -690,6 +714,36 @@
0.5
+
+ https://supabase.com/docs/guides/integrations/zuplo
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/guides/migrations/firebase-auth
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/guides/migrations/firebase-storage
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/guides/migrations/firestore-data
+ weekly
+ 0.5
+
+
+
+ https://supabase.com/docs/guides/migrations/heroku
+ weekly
+ 0.5
+
+
https://supabase.com/docs/guides/platform/compute-add-onsweekly
@@ -768,42 +822,18 @@
0.5
-
- https://supabase.com/docs/learn/auth-deep-dive/auth-deep-dive-jwts
- weekly
- 0.5
-
-
-
- https://supabase.com/docs/learn/auth-deep-dive/auth-google-oauth
- weekly
- 0.5
-
-
-
- https://supabase.com/docs/learn/auth-deep-dive/auth-gotrue
- weekly
- 0.5
-
-
-
- https://supabase.com/docs/learn/auth-deep-dive/auth-policies
- weekly
- 0.5
-
-
-
- https://supabase.com/docs/learn/auth-deep-dive/auth-row-level-security
- weekly
- 0.5
-
-
https://supabase.com/docs/guides/auth/auth-helpers/auth-uiweekly0.5
+
+ https://supabase.com/docs/guides/auth/auth-helpers/nextjs-server-components
+ weekly
+ 0.5
+
+
https://supabase.com/docs/guides/auth/auth-helpers/nextjsweekly
@@ -1740,6 +1770,12 @@
0.5
+
+ https://supabase.com/docs/reference/dart/storage-from-createsignedurls
+ weekly
+ 0.5
+
+
https://supabase.com/docs/reference/dart/storage-from-downloadweekly
@@ -1932,6 +1968,12 @@
0.5
+
+ https://supabase.com/docs/reference/realtime/config
+ weekly
+ 0.5
+
+
https://supabase.com/docs/reference/storage/release-notesweekly
diff --git a/apps/www/.env b/apps/www/.env
index 3f035873262..af6041f3a2c 100644
--- a/apps/www/.env
+++ b/apps/www/.env
@@ -9,3 +9,4 @@ NEXT_PUBLIC_DOCS_URL="http://localhost:3005"
NEXT_PUBLIC_STUDIO_URL="https://localhost:8082"
NEXT_PUBLIC_LAUNCHWEEKSITE_URL="https://localhost:3008"
NEXT_PUBLIC_REFERENCE_DOCS_URL="https://localhost:3010"
+
diff --git a/apps/www/_blog/2020-05-01-supabase-alpha-april-2020.mdx b/apps/www/_blog/2020-05-01-supabase-alpha-april-2020.mdx
index 58416752860..4081fb5f644 100644
--- a/apps/www/_blog/2020-05-01-supabase-alpha-april-2020.mdx
+++ b/apps/www/_blog/2020-05-01-supabase-alpha-april-2020.mdx
@@ -43,12 +43,12 @@ At Supabase, we're building some amazing tools that make Postgres as easy to use
#### Simple interface
Why are database interfaces so hard to use? The Supabase team has built products for 70-year-olds, so we're confident we can make something easier for developers:
-
+
#### Connectors
Send realtime database changes to other systems, like queues or webhooks (Slack notifications!):
-
+
#### And more
diff --git a/apps/www/_blog/2020-06-01-supabase-alpha-may-2020.mdx b/apps/www/_blog/2020-06-01-supabase-alpha-may-2020.mdx
index 7899965f87d..2d04b6b60b8 100644
--- a/apps/www/_blog/2020-06-01-supabase-alpha-may-2020.mdx
+++ b/apps/www/_blog/2020-06-01-supabase-alpha-may-2020.mdx
@@ -39,13 +39,13 @@ OK now for the important part - as promised, we will continue to release our upd
We've revamped the UI to fit in some more features that we're building.
-
+
### Table View
This one is still very unstable, but we wanted to ship it anyway. It's a little hidden away so you'll have to hunt for it :). Give us a couple of months and we promise this will be as good as (better than?) Airtable.
-
+
#### And more
diff --git a/apps/www/_blog/2020-08-02-continuous-postgresql-backup-walg.mdx b/apps/www/_blog/2020-08-02-continuous-postgresql-backup-walg.mdx
index c28d321e737..de0b4c6e801 100644
--- a/apps/www/_blog/2020-08-02-continuous-postgresql-backup-walg.mdx
+++ b/apps/www/_blog/2020-08-02-continuous-postgresql-backup-walg.mdx
@@ -94,7 +94,7 @@ $ sudo -su postgres envdir /etc/wal-g.d/env /usr/local/bin/wal-g backup-push /va
At this point, if you were to check the S3 path that you provided, the following two newly created and populated directories would be observed:
-
+
From then on, subsequent physical backups would be found in the directory `basebackups_005` and any WAL archives would be sent to the directory `wal_005`.
diff --git a/apps/www/_blog/2021-03-11-using-supabase-replit.mdx b/apps/www/_blog/2021-03-11-using-supabase-replit.mdx
index d5f678629be..4b609ee47f1 100644
--- a/apps/www/_blog/2021-03-11-using-supabase-replit.mdx
+++ b/apps/www/_blog/2021-03-11-using-supabase-replit.mdx
@@ -35,11 +35,11 @@ Here's how to start a Supabase + Node.js repl:
Sign up for [replit.com](http://replit.com) and hit new repl in the top left
-
+
Select node.js, give it a name, and click Create repl
-
+
Import supabase's createClient method and hit run to install the required libs:
@@ -56,11 +56,11 @@ const supabase = createClient(
)
```
-
+
Now that supabase is connected you'll want to add some data to your db, you can grab any SQL dataset on the web, or make your own, but the fasted way to test is to open the SQL tab in the Supabase dashboard and click the Countries sample database and click Run.
-
+
From within your repl you can now query your countries table like:
diff --git a/apps/www/_blog/2022-03-29-graphql-now-available.mdx b/apps/www/_blog/2022-03-29-graphql-now-available.mdx
index b09dc44372e..ad9b48e6fae 100644
--- a/apps/www/_blog/2022-03-29-graphql-now-available.mdx
+++ b/apps/www/_blog/2022-03-29-graphql-now-available.mdx
@@ -111,7 +111,7 @@ type Mutation {
): AccountDeleteResponse!
```
-For a complete example with relationships, check out the [reflection docs](https://supabase.github.io/pg_graphql/reflection/).
+For a complete example with relationships, check out the [API docs](https://supabase.github.io/pg_graphql/api/).
## Security
diff --git a/apps/www/_blog/2022-12-01-transparent-column-encryption-with-postgres.mdx b/apps/www/_blog/2022-12-01-transparent-column-encryption-with-postgres.mdx
new file mode 100644
index 00000000000..75c05cf7f08
--- /dev/null
+++ b/apps/www/_blog/2022-12-01-transparent-column-encryption-with-postgres.mdx
@@ -0,0 +1,263 @@
+---
+title: Transparent Column Encryption with Postgres
+description: Using pgsodium's Transparent Column Encryption to encrypt data and provide your users with row-level encryption.
+author: michel
+image: transparent-column-encryption-with-postgres.jpg
+thumb: transparent-column-encryption-with-postgres.jpg
+tags:
+ - postgres
+date: '2022-12-01'
+toc_depth: 3
+---
+
+One of the more common questions we receive at Supabase is “how do I encrypt sensitive data”?
+
+pgsodium's Transparent Column Encryption (TCE) is one of the safest ways, and can be used to encrypt one or more text columns in any number of tables. Using TCE, you can encrypt data so that it doesn't leak into logs as well as providing your users with row-level encryption.
+
+To understand how TCE works, let's first do a deep-dive into an important encryption topic: key derivation.
+
+
+
+ This post is a sneak peek of a big feature we are shipping on LW6. To not miss a thing, get your ticket for [Supabase Launch Week 6!](https://supabase.com/launch-week).
+
+
+
+## How Key Derivation Works
+
+The current state-of-the-art in encryption libraries is [libsodium](https://doc.libsodium.org/).
+
+**libsodium** offers a range of APIs for authenticated secret and public key encryption, key derivation, encrypted streaming, [AEAD](https://en.wikipedia.org/wiki/Authenticated_encryption), various forms of hashing, and much more.
+
+This powerful API is available to PostgreSQL using the [**pgsodium**](https://github.com/michelp/pgsodium) extension. **pgsodium** provides all the functionality of the full **libsodium** API, but previously it required developers to set up database encryption themselves, which remained a challenge even for those familiar with database administration.
+
+To solve this problem, **pgsodium** now has a full key management API, primarily via the table `pgsodium.key` and the `pgsodium.create_key()` function. This key table contains no raw keys, but instead uses libsodium Key IDs to derive keys that are used internally for encryption. A [key derivation function](https://libsodium.gitbook.io/doc/key_derivation) is used with an internal root key that is unavailable to SQL and not stored in the database, but rather managed by you externally using flexible scripts, or by Supabase automatically as part of our service offering.
+
+The simplest way to use **pgsodium** to encrypt and decrypt data is to first create a ******Key ID******. Valid Key IDs are stored in pgsodium in a special extension table, and they can be created using the `pgsodium.create_key()` function. This function takes a number of arguments depending on how it's used, but the simplest case is to create a new key with no arguments:
+
+```sql
+select * from pgsodium.create_key();
+-[ RECORD 1 ]---+-------------------------------------
+id | eaa20d8c-c77c-4985-9f73-2a5f5d1f1e6d
+name |
+status | valid
+key_type | aead-det
+key_id | 2
+key_context | \x7067736f6469756d
+created | 2022-11-13 21:19:35.765823+00
+expires |
+associated_data |
+```
+
+This key can now be used with pgsodium encryption functions by its UUID (`eaa20d8c-c77c-4985-9f73-2a5f5d1f1e6d`). For example:
+
+```sql
+select * from pgsodium.crypto_aead_det_encrypt (
+ 'this is the message', -- a message to encrypt
+ 'this is associated data', -- some authenticated associated data
+ 'eaa20d8c-c77c-4985-9f73-2a5f5d1f1e6d'::uuid -- key ID
+);
+```
+
+This produces the following encrypted “ciphertext” using the `aead-det` algorithm from the libsodium [XChaCha20-SIV](https://github.com/jedisct1/libsodium-xchacha20-siv) encryption function.
+
+```
+-[ RECORD 1 ]-----------+---------------------------------------------------------------------------------------------------------
+crypto_aead_det_encrypt | \\x099baa820250d7375ed141f8f1936af384bc229f3de1010a6eff6ffdaf3998baffbae75b5cd83d1c469407ff2d3764a428b742
+```
+
+Now to decrypt the ciphertext, pass it to the decryption function *with the same Key ID*:
+
+```sql
+select *
+from convert_from(pgsodium.crypto_aead_det_decrypt (
+ '\\x099baa820250d7375ed141f8f1936af384bc229f3de1010a6eff6ffdaf3998baffbae75b5cd83d1c469407ff2d3764a428b742',
+ 'this is associated data',
+ 'eaa20d8c-c77c-4985-9f73-2a5f5d1f1e6d'::uuid
+), 'utf8');
+```
+
+Which recovers the original “plaintext” message:
+
+```
+-[ RECORD 1 ]+--------------------
+convert_from | this is the message
+```
+
+In the above example, there is *no raw key* like shown in the pgcrypto example above, only a Key ID which is used to derive the key used to encrypt the message and authenticate it with the associated data. In fact, it is impossible for a SQL user to derive the key used above, and if the Key ID is stored, then no decrypted information will leak into backups, disk storage, or the database WAL stream.
+
+## Transparent Column Encryption
+
+As of **pgsodium** 3.0.0 and up, the extension offers a simple and declarative Transparent Column Encryption feature (TCE). This feature is now shipped with all Supabase projects. TCE allows you to specify encrypted columns within a table and generates a new view that “wraps” that table to decrypt the contents.
+
+TCE works using two dynamically generated objects for tables that contain encrypted columns:
+
+- an `INSERT UPDATE` trigger that encrypts data when it is inserted or modified
+- a view that is created to wrap the table to decrypt the data when it is accessed
+
+To “transparently” decrypt the table, access the dynamically generated view *instead of the table*. For every encrypted column in the table, the view will have an additional decrypted column that shows the decrypted result.
+
+It's worth noting at this point that sometimes there is some confusion about handling encrypted data with TCE. The `T` stands for ***********Transparent*********** which means, you can always see decrypted data through the view, where the decrypted data can't be see is when stored on disk, or in pg_dumps, backups, WAL streams, etc. This is often called ******************Encryption At Rest****************** and is one layer in many that may be used to encrypt and protect your data.
+
+Often Transparent encryption is understood to be “Transparent Disk Encryption” or “Full Disk Encryption”, this is where a drive is encrypted but reading and writing that drive is decrypted. TCE is similar to this, where data on disk is encrypted, but it is more fine grained, only particular columns are encrypted. The data is also encrypted in the sense that the table stored on disk contains encrypted data, without the view or the key, pg_dumps and backups still contain encrypted data, this is not possible with disk-only encryption.
+
+For the moment TCE only works for columns of type `text` (or types castable to `text` like `json`). Soon we will also support `bytea` and possibly more as use cases and tests get better.
+
+TCE uses one of PostgreSQL's lesser-known features: [`SECURITY LABEL`](https://www.postgresql.org/docs/current/sql-security-label.html). A security label can be thought of as a simple label which is attached to an object (a table, column, etc). Each label is scoped to an extension and that extension can provide security features depending on the label.
+
+Let's see a simple example of using `SECURITY LABEL` to encrypt a column using **pgsodium**.
+
+
+
+Note: We're using “credit cards” in our examples below, because they are very easy to understand. Please don't store credit cards in your Supabase database - we are not a certified PCI Service Provider.
+
+
+
+### One Key ID for the Entire Column
+
+For the simplest case, a column can be encrypted with one Key ID which must be of the type `aead-det` (as created above):
+
+```sql
+CREATE TABLE credit_cards (
+ id bigserial primary key,
+ credit_card_number text
+);
+
+SECURITY LABEL FOR pgsodium
+ ON COLUMN credit_cards.credit_card_number
+ IS 'ENCRYPT WITH KEY ID e348034b-3f07-4878-aad6-000511d12826';
+```
+
+The advantage of this approach is simplicity - the user creates one key and labels a column with it. The cryptographic algorithm for this approach uses a *nonceless* encryption algorithm called `crypto_aead_det_xchacha20()`. This algorithm is written by the author of libsodium and can be found [here](https://github.com/jedisct1/libsodium-xchacha20-siv).
+
+Using one key for an entire column means that whoever can decrypt one row can decrypt them all from a database dump. Also changing (rotating) the key means rewriting the whole table.
+
+### One Key ID per Row
+
+A more fine grained approach would be storing one Key ID per row:
+
+```sql
+CREATE TABLE credit_cards (
+ id bigserial primary key,
+ credit_card_number text,
+ key_id uuid not null DEFAULT 'e348034b-3f07-4878-aad6-000511d12826'::uuid
+);
+
+SECURITY LABEL FOR pgsodium
+ ON COLUMN credit_cards.credit_card_number
+ IS 'ENCRYPT WITH KEY COLUMN key_id';
+```
+
+This approach ensures that a key for one user doesn't necessarily decrypt any others. While rows can share key IDs, they don't necessarily have to (unlike the first example above where one key id was used for the entire column). It also acts as a natural partition that can work in conjunction with Row Level Security to share distinct keys between owners.
+
+Notice also how there is a `DEFAULT` value for the `key_id`. In a way, this gives you the best of both approaches - encrypting the column when a per-row key ID is not provided. The downside to this approach is that you need to store one key ID per row, which takes up more disk space (but that's cheap!).
+
+### One Key ID per Row with Nonce Support
+
+The default cryptographic algorithm for the above approach uses a *nonceless* encryption algorithm called `[crypto_aead_det_xchacha20()](https://github.com/jedisct1/libsodium-xchacha20-siv)`. This algorithm has the advantage that it does not require nonce values, the disadvantage is that duplicate plaintexts will produce duplicate ciphertexts.
+
+Nonces are some extra cryptographic context that is used in many cryptographic algorithms to produce different ciphertexts, even if the plaintexts are the same. The nonce does not have to be secret, but it *does* have to be unique. **pgsodium** comes with a useful function `pgsodium.crypto_aead_det_noncegen()` that will generate a cryptographically secure nonce for you, and in almost all cases it's best to use that function unless you know specifically what you are doing. In password hashing approaches, this is often similar to how a “salt” value is used to deduplicate password hashes.
+
+Duplicate ciphertexts cannot be used to “attack the key”, it can only reveal the duplication. However, duplication is still information. In our examples so far, an attacker might be able to use this information to determine that two accounts share the same credit card number. While not technically breaking the encryption, this still leaks information to an attacker.
+
+```sql
+CREATE TABLE credit_cards (
+ id bigserial primary key,
+ credit_card_number text,
+ key_id uuid not null DEFAULT 'e348034b-3f07-4878-aad6-000511d12826'::uuid,
+ nonce bytea default pgsodium.crypto_aead_det_noncegen()
+);
+
+SECURITY LABEL FOR pgsodium
+ ON COLUMN credit_cards.credit_card_number
+ IS 'ENCRYPT WITH KEY COLUMN key_id NONCE nonce';
+```
+
+This is the most secure form of TCE - there is a unique key ID and a unique nonce per row.
+
+### One Key ID per Row with Associated Data
+
+The encryption that is used for TCE is one of a family of functions provided by **libsodium** to do Authenticated Encryption with Associated Data or [AEAD Encryption](https://en.wikipedia.org/wiki/Authenticated_encryption). The “associated” data is plaintext (unencrypted) information that is mixed into the authentication signature of the encrypted data, such that when you authenticate the data, you also know that the associated data is authentic.
+
+AEAD is helpful because often you have metadata associated with a secret, which isn't confidential but must not be forged.
+
+In our credit card example, we might associate a "`credit_card_number`" with an "`account_id`". But what if a malicious actor wanted to use someone else's credit card on their own account? If someone could forge the `account_id` data column, swapping an `account_id` with their own `account_id`, then you could be tricked into using the wrong credit card. By “associating” the `account_id` with the `credit_card_number`, it cannot be forged without throwing an error.
+
+Like above, this is done simply by extending the security label with the associated data column:
+
+```sql
+CREATE TABLE credit_cards (
+ id bigserial primary key,
+ credit_card_number text,
+ account_id integer,
+ key_id uuid not null DEFAULT 'e348034b-3f07-4878-aad6-000511d12826'::uuid,
+ nonce bytea default pgsodium.crypto_aead_det_noncegen()
+);
+
+SECURITY LABEL FOR pgsodium
+ ON COLUMN credit_cards.credit_card_number
+ IS 'ENCRYPT WITH KEY COLUMN key_id ASSOCIATED (account_id) NONCE nonce';
+```
+
+The new label indicates which column is to be associated with the secret, and that's it! Your `account_id` and credit card number are now protected under the same authentication signature as the secret itself.
+
+## Using an Encrypted Table
+
+Now that you have TCE setup for a table, it's easy to use by simply inserting data into the table, and querying that data by looking at its generated view. The view is named `decrypted_` and by default is in the same schema as your table:
+
+```sql
+INSERT INTO credit_cards (
+ credit_card_number,
+ account_id
+)
+VALUES
+ ('1234-5678-8765-4321', 123);
+```
+
+Now that you have inserted data, look at the table and notice how the credit card number is encrypted. This is the data that is stored on disk, the encrypted card number, the key id, and the account id, ********************************but the key itself is not stored********************************. This means if someone gets a backup or dump of your database, they cannot decrypt the credit card number, they do not have the key, only the key ID:
+
+```sql
+> select * from credit_cards where account_id = 123;
+-[ RECORD 1 ]------+---------------------------------------------------------------------
+id | 1
+credit_card_number | jf8KfImkKTr+j4gzyDZQtLDEFL9eSlFuKjNlNEJvDg+OIKUr2wjF/8NnYcLisb5F9xiN
+account_id | 123
+key_id | 7f753c4f-8c68-457a-8801-1798b2e9f44d
+nonce | \x300a14aa721184ff7cf0f6bf088da267
+```
+
+For you, the developer, you need the unencrypted credit card number for you application. No problem, you can access that data using the dynamically generated decryption view `decrypted_credit_cards`:
+
+```sql
+> select * from decrypted_credit_cards where account_id = 123;
+-[ RECORD 1 ]----------------+---------------------------------------------------------------------
+id | 1
+credit_card_number | jf8KfImkKTr+j4gzyDZQtLDEFL9eSlFuKjNlNEJvDg+OIKUr2wjF/8NnYcLisb5F9xiN
+decrypted_credit_card_number | 1234-5678-8765-4321
+account_id | 123
+key_id | 7f753c4f-8c68-457a-8801-1798b2e9f44d
+nonce | \x300a14aa721184ff7cf0f6bf088da267
+```
+
+Notice how there is a new column called `decrypted_credit_card_number`. This column is not stored in database or on disk at all, it is generated “on-the-fly” as you select from the view. Database dumps do not contain this information, only the view itself, and most importantly, *************************************raw decryption keys are never stored*************************************.
+
+## Future possibilities
+
+We're always thinking about the future possibilities for features and tools that we can bring to the PostgreSQL community, and we'd love to hear from you about what kind of encryption features you'd like to see. Some things we've considered but not yet explored yet are:
+
+- Built-in Key Management Server (KMS) with REST API ala AWS or GCP.
+- Seamless Integration with external KMS services for key management.
+- Signcryption-based Token formats for exchanging AEAD tokens.
+- End-to-End encryption for user data using libsodium's `crypto_secretstream()` API.
+- Group encryption using [signcryption](https://github.com/jedisct1/libsodium-signcryption)
+- Your idea here?
+
+There's a lot of potential in the world of cryptography with Postgres and pgsodium, and we'd love to hear any ideas you may have as well. Join us in our [Discord #encryption channel](https://discord.com/channels/839993398554656828/1009906326480101417) if you want to chat more about it with us!
+
+## More Postgres Resources
+
+- [Postgres Full Text Search vs the rest](https://supabase.com/blog/postgres-full-text-search-vs-the-rest)
+- [Postgres WASM by Snaplet and Supabase](https://supabase.com/blog/postgres-wasm)
+- [Choosing a Postgres Primary Key](https://supabase.com/blog/choosing-a-postgres-primary-key)
+- [Implementing "seen by" functionality with Postgres](https://supabase.com/blog/seen-by-in-postgresql)
+- [Partial data dumps using Postgres Row Level Security](https://supabase.com/blog/partial-postgresql-data-dumps-with-rls)
+- [Realtime Postgres RLS on Supabase](https://supabase.com/blog/realtime-row-level-security-in-postgresql)
diff --git a/apps/www/components/Globe.tsx b/apps/www/components/Globe.tsx
new file mode 100644
index 00000000000..388c6a3d65d
--- /dev/null
+++ b/apps/www/components/Globe.tsx
@@ -0,0 +1,72 @@
+import createGlobe from 'cobe'
+import { useEffect, useRef } from 'react'
+import { useTheme } from './Providers/index'
+
+const Globe = () => {
+ const { isDarkMode } = useTheme()
+ const canvasRef = useRef()
+
+ useEffect(() => {
+ let rotation: number = 0
+ let width: number = 0
+ const onResize = () => canvasRef.current && (width = canvasRef.current.offsetWidth)
+ window.addEventListener('resize', onResize)
+ onResize()
+ const cobe = createGlobe(canvasRef.current, {
+ devicePixelRatio: 2,
+ width: width * 2,
+ height: width * 2,
+ phi: 0,
+ theta: 0.3,
+ dark: isDarkMode ? 1 : 0,
+ diffuse: 3,
+ scale: 1,
+ opacity: 0.8,
+ mapSamples: 20000,
+ mapBrightness: 4,
+ baseColor: [255 / 255, 255 / 255, 255 / 255],
+ markerColor: [62 / 255, 207 / 255, 142 / 255],
+ glowColor: [255 / 255, 255 / 255, 255 / 255],
+ markers: [
+ { location: [53.4084, 2.9916], size: 0.06 },
+ { location: [1.3521, 103.8198], size: 0.06 },
+ { location: [-40.9006, 174.886], size: 0.06 },
+ { location: [14.0583, 108.2772], size: 0.06 },
+ { location: [37.7749, -122.4194], size: 0.06 },
+ { location: [41.3874, 2.1686], size: 0.06 },
+ { location: [49.2827, -123.1207], size: 0.06 },
+ { location: [-36.9848, 143.3906], size: 0.06 },
+ { location: [42.3601, -71.0589], size: 0.06 },
+ { location: [52.52, 13.405], size: 0.06 },
+ { location: [33.749, -84.388], size: 0.06 },
+ { location: [35.6762, 139.6503], size: 0.06 },
+ { location: [9.19, -75.0152], size: 0.06 },
+ { location: [-25.2521, -52.0215], size: 0.06 },
+ ],
+ onRender: (state) => {
+ state.phi = rotation
+ rotation += 0.0025
+ state.width = width * 2
+ state.height = width * 2
+ },
+ })
+ setTimeout(() => (canvasRef.current.style.opacity = '1'))
+ return () => cobe.destroy()
+ }, [isDarkMode])
+
+ return (
+
+ )
+}
+
+export default Globe
diff --git a/apps/www/components/LaunchWeek/Ticket/TicketContainer.tsx b/apps/www/components/LaunchWeek/Ticket/TicketContainer.tsx
index 632c731bfc4..f658037f136 100644
--- a/apps/www/components/LaunchWeek/Ticket/TicketContainer.tsx
+++ b/apps/www/components/LaunchWeek/Ticket/TicketContainer.tsx
@@ -33,23 +33,17 @@ export default function Conf({
setPageState,
}}
>
-
- {pageState === 'registration' && !sharePage ? (
- <>
- {/* */}
-
- {/* */}
- >
- ) : (
-
- )}
-
+ {pageState === 'registration' && !sharePage ? (
+
+ ) : (
+
+ )}
)
}
diff --git a/apps/www/components/LaunchWeek/Ticket/conf-container.module.css b/apps/www/components/LaunchWeek/Ticket/conf-container.module.css
index 2a7eac88ac0..3f9f3a79e46 100644
--- a/apps/www/components/LaunchWeek/Ticket/conf-container.module.css
+++ b/apps/www/components/LaunchWeek/Ticket/conf-container.module.css
@@ -1,4 +1,4 @@
-.container {
+/* .container {
margin: auto 0;
padding: 0 var(--space-4x);
}
@@ -8,4 +8,4 @@
margin: auto;
padding: 0 var(--space-8x);
}
-}
+} */
diff --git a/apps/www/components/LaunchWeek/Ticket/conf-container.tsx b/apps/www/components/LaunchWeek/Ticket/conf-container.tsx
index e331c3e882c..227361424b9 100644
--- a/apps/www/components/LaunchWeek/Ticket/conf-container.tsx
+++ b/apps/www/components/LaunchWeek/Ticket/conf-container.tsx
@@ -1,5 +1,5 @@
import styles from './conf-container.module.css'
export default function ConfContainer({ children }: { children: React.ReactNode }) {
- return
) : golden ? (
<>
- You won a golden ticket! Claim it now!
+ You got a Golden ticket. This means you’re in, and you also won a Supabase goodie
+ bag. Tweet it to redeem your swag pack!
>
+ ) : username ? (
+ <>Here is your unique ticket image for bragging on socials!>
) : (
<>
- You're in. Make it unique.
+ This means you're in. Generate a unique ticket image with your GitHub profile, cause
+ a few of the lucky attendees will get a limited edition Supabase goodie bag. Make
+ sure you don't skip your chance.
>
)}
-
- {sharePage ? (
- <>
- Join {name ?? 'us'} on {DATE}.
- >
- ) : golden ? (
- <>Claim your ticket with GitHub and Tweet it to redeem your swag pack!>
- ) : (
- <>
- Generate a unique ticket image with
- your GitHub profile.
- >
- )}
-
+ Working in a globally distributed team is rewarding but has its challenges. We
+ are across many different timezones, so we use tools like Notion, Slack, and
+ Discord to stay connected to our team, and our community.
+
+
+
+
+ We deeply believe in the efficacy of collaborative open source
+
+
+
+
+
+
+
+
+
+
+
+ What is Supabase
+
+
+ Supabase is an open source Firebase alternative, built by developers for
+ developers. Supabase adds auth, realtime, storage, restful APIs, and edge
+ functions to Postgres without a single line of code. Supabase was
+ born-remote. Having a globally distributed, open source company is our
+ secret weapon to hiring top-tier talent.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Human powered
+
+
+ As a completely remote and asynchronous team, we focus on these five traits to
+ keep our team effective:
+
+ We're building a community of communities, bringing together developers from
+ many different backgrounds, as well as new developers looking to get involved
+ with open source. We love celebrating everyone who contributes their time to the
+ Supabase mission.
+
+ The entire process is fully remote and all communication happens over email or via
+ video chat in Google. Meet. The calls are all 1:1 and usually take between 20-45
+ minutes. We know you are interviewing us too, so please ask questions. We are happy
+ to answer.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/www/public/images/career/icons/crisis_control-light.svg b/apps/www/public/images/career/icons/crisis_control-light.svg
new file mode 100644
index 00000000000..ea1c63faf26
--- /dev/null
+++ b/apps/www/public/images/career/icons/crisis_control-light.svg
@@ -0,0 +1,8 @@
+
diff --git a/apps/www/public/images/career/icons/curious-dark.svg b/apps/www/public/images/career/icons/curious-dark.svg
new file mode 100644
index 00000000000..3ac482aab8c
--- /dev/null
+++ b/apps/www/public/images/career/icons/curious-dark.svg
@@ -0,0 +1,8 @@
+
diff --git a/apps/www/public/images/career/icons/curious-light.svg b/apps/www/public/images/career/icons/curious-light.svg
new file mode 100644
index 00000000000..a2c8ca8dc69
--- /dev/null
+++ b/apps/www/public/images/career/icons/curious-light.svg
@@ -0,0 +1,8 @@
+
diff --git a/apps/www/public/images/career/icons/education-dark.svg b/apps/www/public/images/career/icons/education-dark.svg
new file mode 100644
index 00000000000..db5c8c1153f
--- /dev/null
+++ b/apps/www/public/images/career/icons/education-dark.svg
@@ -0,0 +1,8 @@
+
diff --git a/apps/www/public/images/career/icons/education-light.svg b/apps/www/public/images/career/icons/education-light.svg
new file mode 100644
index 00000000000..3e92aca7bff
--- /dev/null
+++ b/apps/www/public/images/career/icons/education-light.svg
@@ -0,0 +1,8 @@
+
diff --git a/apps/www/public/images/career/icons/egoless-dark.svg b/apps/www/public/images/career/icons/egoless-dark.svg
new file mode 100644
index 00000000000..0ac7b4e3303
--- /dev/null
+++ b/apps/www/public/images/career/icons/egoless-dark.svg
@@ -0,0 +1,23 @@
+
diff --git a/apps/www/public/images/career/icons/egoless-light.svg b/apps/www/public/images/career/icons/egoless-light.svg
new file mode 100644
index 00000000000..62ca09e67b9
--- /dev/null
+++ b/apps/www/public/images/career/icons/egoless-light.svg
@@ -0,0 +1,23 @@
+
diff --git a/apps/www/public/images/career/icons/flexibility-dark.svg b/apps/www/public/images/career/icons/flexibility-dark.svg
new file mode 100644
index 00000000000..bfb0cc7d462
--- /dev/null
+++ b/apps/www/public/images/career/icons/flexibility-dark.svg
@@ -0,0 +1,18 @@
+
diff --git a/apps/www/public/images/career/icons/flexibility-light.svg b/apps/www/public/images/career/icons/flexibility-light.svg
new file mode 100644
index 00000000000..45f5cb66136
--- /dev/null
+++ b/apps/www/public/images/career/icons/flexibility-light.svg
@@ -0,0 +1,18 @@
+
diff --git a/apps/www/public/images/career/icons/globe-dark.svg b/apps/www/public/images/career/icons/globe-dark.svg
new file mode 100644
index 00000000000..b5182ca85ab
--- /dev/null
+++ b/apps/www/public/images/career/icons/globe-dark.svg
@@ -0,0 +1,3 @@
+
diff --git a/apps/www/public/images/career/icons/healthcare-dark.svg b/apps/www/public/images/career/icons/healthcare-dark.svg
new file mode 100644
index 00000000000..946324be4fc
--- /dev/null
+++ b/apps/www/public/images/career/icons/healthcare-dark.svg
@@ -0,0 +1,10 @@
+
diff --git a/apps/www/public/images/career/icons/healthcare-light.svg b/apps/www/public/images/career/icons/healthcare-light.svg
new file mode 100644
index 00000000000..3fa16cfd2b1
--- /dev/null
+++ b/apps/www/public/images/career/icons/healthcare-light.svg
@@ -0,0 +1,10 @@
+
diff --git a/apps/www/public/images/career/icons/honesty-dark.svg b/apps/www/public/images/career/icons/honesty-dark.svg
new file mode 100644
index 00000000000..5f7a3c5f16a
--- /dev/null
+++ b/apps/www/public/images/career/icons/honesty-dark.svg
@@ -0,0 +1,8 @@
+
diff --git a/apps/www/public/images/career/icons/honesty-light.svg b/apps/www/public/images/career/icons/honesty-light.svg
new file mode 100644
index 00000000000..b6fa609b7e8
--- /dev/null
+++ b/apps/www/public/images/career/icons/honesty-light.svg
@@ -0,0 +1,8 @@
+
diff --git a/apps/www/public/images/career/icons/jobs-dark.svg b/apps/www/public/images/career/icons/jobs-dark.svg
new file mode 100644
index 00000000000..206189f35b7
--- /dev/null
+++ b/apps/www/public/images/career/icons/jobs-dark.svg
@@ -0,0 +1,22 @@
+
diff --git a/apps/www/public/images/career/icons/jobs-light.svg b/apps/www/public/images/career/icons/jobs-light.svg
new file mode 100644
index 00000000000..ff571524680
--- /dev/null
+++ b/apps/www/public/images/career/icons/jobs-light.svg
@@ -0,0 +1,22 @@
+
diff --git a/apps/www/public/images/career/icons/kaizen-dark.svg b/apps/www/public/images/career/icons/kaizen-dark.svg
new file mode 100644
index 00000000000..6f227f02088
--- /dev/null
+++ b/apps/www/public/images/career/icons/kaizen-dark.svg
@@ -0,0 +1,10 @@
+
diff --git a/apps/www/public/images/career/icons/kaizen-light.svg b/apps/www/public/images/career/icons/kaizen-light.svg
new file mode 100644
index 00000000000..108993c5a30
--- /dev/null
+++ b/apps/www/public/images/career/icons/kaizen-light.svg
@@ -0,0 +1,10 @@
+
diff --git a/apps/www/public/images/career/icons/offsite-dark.svg b/apps/www/public/images/career/icons/offsite-dark.svg
new file mode 100644
index 00000000000..9c4b5d01426
--- /dev/null
+++ b/apps/www/public/images/career/icons/offsite-dark.svg
@@ -0,0 +1,7 @@
+
diff --git a/apps/www/public/images/career/icons/offsite-light.svg b/apps/www/public/images/career/icons/offsite-light.svg
new file mode 100644
index 00000000000..c1b916f83fd
--- /dev/null
+++ b/apps/www/public/images/career/icons/offsite-light.svg
@@ -0,0 +1,7 @@
+
diff --git a/apps/www/public/images/career/icons/open_source-dark.svg b/apps/www/public/images/career/icons/open_source-dark.svg
new file mode 100644
index 00000000000..a499faa7824
--- /dev/null
+++ b/apps/www/public/images/career/icons/open_source-dark.svg
@@ -0,0 +1,24 @@
+
diff --git a/apps/www/public/images/career/icons/open_source-light.svg b/apps/www/public/images/career/icons/open_source-light.svg
new file mode 100644
index 00000000000..f2eeb46e1db
--- /dev/null
+++ b/apps/www/public/images/career/icons/open_source-light.svg
@@ -0,0 +1,24 @@
+
diff --git a/apps/www/public/images/career/icons/process-dark.svg b/apps/www/public/images/career/icons/process-dark.svg
new file mode 100644
index 00000000000..b88fa4929da
--- /dev/null
+++ b/apps/www/public/images/career/icons/process-dark.svg
@@ -0,0 +1,10 @@
+
diff --git a/apps/www/public/images/career/icons/process-light.svg b/apps/www/public/images/career/icons/process-light.svg
new file mode 100644
index 00000000000..84a2624f4ae
--- /dev/null
+++ b/apps/www/public/images/career/icons/process-light.svg
@@ -0,0 +1,10 @@
+
diff --git a/apps/www/public/images/career/icons/remote-dark.svg b/apps/www/public/images/career/icons/remote-dark.svg
new file mode 100644
index 00000000000..48870018b63
--- /dev/null
+++ b/apps/www/public/images/career/icons/remote-dark.svg
@@ -0,0 +1,10 @@
+
diff --git a/apps/www/public/images/career/icons/remote-light.svg b/apps/www/public/images/career/icons/remote-light.svg
new file mode 100644
index 00000000000..048f9285284
--- /dev/null
+++ b/apps/www/public/images/career/icons/remote-light.svg
@@ -0,0 +1,10 @@
+
diff --git a/apps/www/public/images/career/icons/tech_allowance-dark.svg b/apps/www/public/images/career/icons/tech_allowance-dark.svg
new file mode 100644
index 00000000000..474a9af89f5
--- /dev/null
+++ b/apps/www/public/images/career/icons/tech_allowance-dark.svg
@@ -0,0 +1,9 @@
+
diff --git a/apps/www/public/images/career/icons/tech_allowance-light.svg b/apps/www/public/images/career/icons/tech_allowance-light.svg
new file mode 100644
index 00000000000..8770d71bcec
--- /dev/null
+++ b/apps/www/public/images/career/icons/tech_allowance-light.svg
@@ -0,0 +1,9 @@
+
diff --git a/apps/www/public/images/career/icons/time_off-light.svg b/apps/www/public/images/career/icons/time_off-light.svg
new file mode 100644
index 00000000000..a7c6674a0d2
--- /dev/null
+++ b/apps/www/public/images/career/icons/time_off-light.svg
@@ -0,0 +1,8 @@
+
diff --git a/apps/www/public/images/launchweek/2x-gold-ticket-background.jpg b/apps/www/public/images/launchweek/2x-gold-ticket-background.jpg
new file mode 100644
index 00000000000..2458ba6e641
Binary files /dev/null and b/apps/www/public/images/launchweek/2x-gold-ticket-background.jpg differ
diff --git a/apps/www/public/images/launchweek/2x-regular-ticket-background.jpg b/apps/www/public/images/launchweek/2x-regular-ticket-background.jpg
new file mode 100644
index 00000000000..e2bcd23470e
Binary files /dev/null and b/apps/www/public/images/launchweek/2x-regular-ticket-background.jpg differ
diff --git a/apps/www/public/images/launchweek/gold-ticket-background.png b/apps/www/public/images/launchweek/gold-ticket-background.png
new file mode 100644
index 00000000000..5cdf8d40a9c
Binary files /dev/null and b/apps/www/public/images/launchweek/gold-ticket-background.png differ
diff --git a/apps/www/public/images/launchweek/golden-ticket.svg b/apps/www/public/images/launchweek/golden-ticket.svg
new file mode 100644
index 00000000000..9f4bebcf11e
--- /dev/null
+++ b/apps/www/public/images/launchweek/golden-ticket.svg
@@ -0,0 +1,70 @@
+
diff --git a/apps/www/public/images/launchweek/green-ticket.svg b/apps/www/public/images/launchweek/green-ticket.svg
new file mode 100644
index 00000000000..350ca923c70
--- /dev/null
+++ b/apps/www/public/images/launchweek/green-ticket.svg
@@ -0,0 +1,63 @@
+
diff --git a/apps/www/public/images/launchweek/launch-week-6.jpg b/apps/www/public/images/launchweek/launch-week-6.jpg
new file mode 100644
index 00000000000..cf7c77d3cda
Binary files /dev/null and b/apps/www/public/images/launchweek/launch-week-6.jpg differ
diff --git a/apps/www/public/images/launchweek/launchweek-logo--dark.svg b/apps/www/public/images/launchweek/launchweek-logo--dark.svg
index 90d87475b6f..5a3f62beff2 100644
--- a/apps/www/public/images/launchweek/launchweek-logo--dark.svg
+++ b/apps/www/public/images/launchweek/launchweek-logo--dark.svg
@@ -1,11 +1,20 @@
-
+ >
),
{
- width: 2000,
- height: 1000,
+ width: 1200,
+ height: 630,
fonts: [
{
name: 'Circular',
@@ -190,7 +207,8 @@ export async function handler(req: Request) {
const { error } = await supabaseAdminClient.storage
.from('images')
.upload(`lw6/tickets/${username}.png`, generatedImage.body!, {
- // cacheControl: '31536000', // TODO add for prod
+ contentType: 'image/png',
+ cacheControl: '31536000',
upsert: false,
})
if (error) throw error
diff --git a/examples/edge-functions/supabase/functions/puppeteer/README.md b/examples/edge-functions/supabase/functions/puppeteer/README.md
new file mode 100644
index 00000000000..353df27fd05
--- /dev/null
+++ b/examples/edge-functions/supabase/functions/puppeteer/README.md
@@ -0,0 +1,11 @@
+# Using Puppeteer
+
+This example shows how you can use Puppeteer and a headless-browser to generate screenshots of a web page. Pass the `url` of the web page as a query string.
+
+Since Edge Functions cannot run a Headless Browser instance due to resource constraints, you will need to use a hosted browser service like https://browserless.io.
+
+## Deploy
+
+```bash
+supabase functions deploy puppeteer --no-verify-jwt
+```
diff --git a/examples/edge-functions/supabase/functions/puppeteer/index.ts b/examples/edge-functions/supabase/functions/puppeteer/index.ts
new file mode 100644
index 00000000000..7eac140f29f
--- /dev/null
+++ b/examples/edge-functions/supabase/functions/puppeteer/index.ts
@@ -0,0 +1,25 @@
+import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
+import puppeteer from 'https://deno.land/x/puppeteer@16.2.0/mod.ts'
+
+serve(async (req) => {
+ try {
+ // Visit browserless.io to get your free API token
+ const browser = await puppeteer.connect({
+ browserWSEndpoint: 'wss://chrome.browserless.io?token=YOUR_API_TOKEN',
+ })
+ const page = await browser.newPage()
+
+ const url = new URL(req.url).searchParams.get('url') || 'http://www.example.com'
+
+ await page.goto(url)
+ const screenshot = await page.screenshot()
+
+ return new Response(screenshot, { headers: { 'Content-Type': 'image/png' } })
+ } catch (e) {
+ console.error(e)
+ return new Response(JSON.stringify(`Error occurred when generating the screenshot`), {
+ headers: { 'Content-Type': 'application/json' },
+ status: 500,
+ })
+ }
+})
diff --git a/examples/edge-functions/supabase/functions/stripe-webhooks/index.ts b/examples/edge-functions/supabase/functions/stripe-webhooks/index.ts
index 3379e6060e8..6b159a2a5b4 100644
--- a/examples/edge-functions/supabase/functions/stripe-webhooks/index.ts
+++ b/examples/edge-functions/supabase/functions/stripe-webhooks/index.ts
@@ -5,7 +5,7 @@
import { serve } from 'https://deno.land/std@0.132.0/http/server.ts'
// esm.sh is used to compile stripe-node to be compatible with ES modules.
-import Stripe from 'https://esm.sh/stripe@10.13.0?target=deno&deno-std=0.132.0'
+import Stripe from 'https://esm.sh/stripe@10.13.0?target=deno&no-check&deno-std=0.132.0'
const stripe = Stripe(Deno.env.get('STRIPE_API_KEY'), {
// This is needed to use the Fetch API rather than relying on the Node http
diff --git a/i18n/README.pt-br.md b/i18n/README.pt-br.md
index f16daefe69c..fa90c744008 100644
--- a/i18n/README.pt-br.md
+++ b/i18n/README.pt-br.md
@@ -109,7 +109,7 @@ Nossa biblioteca de cliente é modular. Cada sub-biblioteca é uma implementaç