No files matched your search
@@ -1,45 +1,47 @@
|
||||
# Developing Supabase
|
||||
|
||||
1. [Development setup](#development-setup)
|
||||
- [Install dependencies](#install-dependencies)
|
||||
- [Fork the repository](#fork-the-repository)
|
||||
1. [Build Supabase](#build-supabase)
|
||||
- [Choose a directory](#choose-a-directory)
|
||||
1. [Start a development server](#start-a-development-server)
|
||||
- [Supabase Website](#supabase-website)
|
||||
- [Supabase Docs](#supabase-docs)
|
||||
- [Supabase Studio](#supabase-studio)
|
||||
1. [Create a pull request](#create-a-pull-request)
|
||||
1. [Getting started](#getting-started)
|
||||
- [Install dependencies](#install-dependencies)
|
||||
2. [Local development](#local-development)
|
||||
- [Fork the repository](#fork-the-repository)
|
||||
- [Clone the repo](#clone-the-repo)
|
||||
- [Running turborepo](#running-turborepo)
|
||||
- [Shared components](#shared-components)
|
||||
- [Installing packages](#installing-packages)
|
||||
3. [Create a pull request](#create-a-pull-request)
|
||||
|
||||
- [Common tasks](#common-tasks)
|
||||
- [Add a redirect](#add-a-redirect)
|
||||
- [Monorepo](#monorepo)
|
||||
- [Getting started](#getting-started)
|
||||
- [Shared components](#shared-components)
|
||||
- [Installing packages](#installing-packages)
|
||||
- [Development](#development)
|
||||
- [Community channels](#community-channels)
|
||||
|
||||
## Development setup
|
||||
## Getting started
|
||||
|
||||
Thanks for your interest in Supabase and for wanting to contribute! Before you begin, read the
|
||||
Thanks for your interest in [Supabase](https://supabase.com) and for wanting to contribute! Before you begin, read the
|
||||
[code of conduct](https://github.com/supabase/.github/blob/main/CODE_OF_CONDUCT.md) and check out the
|
||||
[existing issues](https://github.com/supabase/supabase/issues).
|
||||
This document describes how to set up your development environment to build and test Supabase.
|
||||
This document describes how to set up your development environment to build and test [Supabase](https://supabase.com).
|
||||
|
||||
### Install dependencies
|
||||
|
||||
You need to install and configure the following dependencies on your machine to build Supabase:
|
||||
You need to install and configure the following dependencies on your machine to build [Supabase](https://supabase.com):
|
||||
|
||||
- [Git](http://git-scm.com/)
|
||||
- [Node.js v16.x (LTS)](http://nodejs.org)
|
||||
- [npm](https://www.npmjs.com/) version 7+ or [Yarn](https://yarnpkg.com/)
|
||||
|
||||
## Local development
|
||||
|
||||
We are in the process of migrating this repository to monorepo, using [Turborepo](https://turborepo.org/docs).
|
||||
|
||||
Eventually, all the apps will be run using [Turborepo](https://turborepo.org/docs), which will significantly improve the developer workflow.
|
||||
|
||||
If you are working on the ([docs site](https://supabase.com/docs)), please refer to this [developers guide](https://github.com/supabase/supabase/tree/master/apps/reference/DEVELOPERS.md).
|
||||
|
||||
### Fork the repository
|
||||
|
||||
To contribute code to Supabase, you must fork the [Supabase Repository](https://github.com/supabase/supabase).
|
||||
To contribute code to [Supabase](https://supabase.com), you must fork the [Supabase Repository](https://github.com/supabase/supabase).
|
||||
|
||||
## Build Supabase
|
||||
### Clone the repo
|
||||
|
||||
1. Clone your GitHub forked repository:
|
||||
|
||||
@@ -52,104 +54,61 @@ To contribute code to Supabase, you must fork the [Supabase Repository](https://
|
||||
cd supabase
|
||||
```
|
||||
|
||||
### Choose a directory
|
||||
### Running turborepo
|
||||
|
||||
Choose if you want to work on the [Supabase Website](https://supabase.com), [Supabase Docs](https://supabase.com/docs), or [Supabase Studio](https://app.supabase.com).
|
||||
[Supabase](https://supabase.com) uses [Turborepo](https://turborepo.org/docs) to manage and run this monorepo.
|
||||
|
||||
1. Go to the [Supabase Website](https://supabase.com) directory
|
||||
1. Install the dependences in the root of the repo.
|
||||
|
||||
```sh
|
||||
cd apps/www
|
||||
npm install # install dependencies
|
||||
```
|
||||
|
||||
Go to the [Supabase Docs](https://supabase.com/docs) directory
|
||||
|
||||
2. You can then run the apps simultaneously with the following.
|
||||
```sh
|
||||
cd apps/docs
|
||||
npm run dev # start all the applications
|
||||
```
|
||||
|
||||
Go to the [Supabase Studio](https://app.supabase.com) directory
|
||||
Then visit, and edit, any of the following sites:
|
||||
|
||||
```sh
|
||||
cd studio
|
||||
```
|
||||
| Site | Directory | Scope name | Description | Local development server |
|
||||
| -------------------------------------------- | -------------- | ---------- | ------------------------------------ | ------------------------ |
|
||||
| [supabase.com](https://supabase.com) | `/apps/www` | www | The main website | http://localhost:3000 |
|
||||
| [app.supabase.com](https://app.supabase.com) | `/studio` | studio | Studio dashboard | http://localhost:8082 |
|
||||
| [supabase.com/docs] Next.js based Docs site | `/apps/docs` . | docs | Guides and Reference (Next.js based) | http://localhost:3001 |
|
||||
|
||||
1. Install npm/yarn dependencies:
|
||||
#### Running sites individually
|
||||
|
||||
npm
|
||||
You can run any of the sites indiviudally by using the scope name. For example:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
```sh
|
||||
npm run dev:www
|
||||
```
|
||||
|
||||
or with yarn
|
||||
#### Shared components
|
||||
|
||||
```sh
|
||||
yarn install
|
||||
```
|
||||
The monorepo has a set of shared components under `/packages`:
|
||||
|
||||
## Start a development server
|
||||
- `/packages/common`: Common React code, shared between all sites.
|
||||
- `/packages/config`: All shared config
|
||||
- `/packages/spec`: Generates documentation using spec files.
|
||||
- `/packages/tsconfig`: Shared Typescript settings
|
||||
|
||||
To debug code and to see your changes in real time, it is often useful to have a local HTTP server. Click one of the three links below to choose which development server you want to start.
|
||||
#### Installing packages
|
||||
|
||||
- [Supabase Website](#supabase-website)
|
||||
- [Supabase Docs](#supabase-docs)
|
||||
- [Supabase Studio](#supabase-studio)
|
||||
Installing a package with NPM workspaces requires you to add the `-w` flag to tell NPM which workspace you want to install into. Do not install dependencies in their local folder, install them from the route using the `-w` flag.
|
||||
|
||||
### Supabase Website
|
||||
The format is: `npm install <package name> -w=<workspace to install in>`.
|
||||
|
||||
The website is moving to a new monorepo setup. See the [Monorepo](#monorepo) section below.
|
||||
For example:
|
||||
|
||||
### Supabase Docs
|
||||
- `npm install react -w common`: installs into `./packages/common`
|
||||
- `npm install react -w www`: installs into `./apps/www`
|
||||
- `npm install react -w studio`: installs into `./studio`
|
||||
|
||||
1. Build development server
|
||||
You do not need to install `devDependencies` in each workspace. These can all be installed in the root package.
|
||||
|
||||
npm
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
or with yarn
|
||||
|
||||
```sh
|
||||
yarn build
|
||||
```
|
||||
|
||||
1. Start development server
|
||||
|
||||
npm
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
or with yarn
|
||||
|
||||
```sh
|
||||
yarn dev
|
||||
```
|
||||
|
||||
1. Access the local server in your web browser at http://localhost:3001/docs.
|
||||
|
||||
### Supabase Studio
|
||||
|
||||
1. Start development server
|
||||
|
||||
npm
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
or with yarn
|
||||
|
||||
```sh
|
||||
yarn dev
|
||||
```
|
||||
|
||||
1. Access the local server in your web browser at http://localhost:8082/.
|
||||
See the [Supabase Studio readme](./studio/README.md) for more information.
|
||||
---
|
||||
|
||||
## Create a pull request
|
||||
|
||||
@@ -165,63 +124,6 @@ Did you have an issue, like a merge conflict, or don't know how to open a pull r
|
||||
|
||||
Create a new entry in the [`redirects.js`](https://github.com/supabase/supabase/blob/master/apps/www/lib/redirects.js) file in our main site.
|
||||
|
||||
## Monorepo
|
||||
|
||||
We are in the process of migrating this repository to monorepo, using Turborepo.
|
||||
Eventually, the docs and the Studio will be run using Turborepo, which will significantly improve the developer workflow.
|
||||
You must be using NPM 7 or higher.
|
||||
|
||||
### Getting started
|
||||
|
||||
```sh
|
||||
npm install # install dependencies
|
||||
npm run dev # start all the applications
|
||||
```
|
||||
|
||||
Then edit and visit any of the following sites:
|
||||
|
||||
Site | Directory | Description | Local development server
|
||||
---- | --------- | ----------- | ------------------------
|
||||
[supabase.com](https://supabase.com) | `/apps/www` | The main website | http://localhost:3000
|
||||
[supabase.com/docs](https://supabase.com/docs) | `apps/docs` | Guides and Reference documentation | http://localhost:3001/docs
|
||||
[POC] Community forum | `/apps/temp-community-forum` | GitHub Discussions in a Next.js site | http://localhost:3002
|
||||
[POC] DEV articles site | `/apps/temp-community-tutorials` | A Next.js site for our DEV articles (which community members can write) | http://localhost:3003
|
||||
|
||||
### Shared components
|
||||
|
||||
The monorepo has a set of shared components under `/packages`:
|
||||
|
||||
- `/packages/common`: Common React code, shared between all sites.
|
||||
- `/packages/config`: All shared config
|
||||
- `/packages/spec`: Generates documentation using spec files.
|
||||
- `/packages/tsconfig`: Shared Typescript settings
|
||||
- `/packages/ui`: Shared UI components (formerly @supabase/ui)
|
||||
|
||||
To use these 'packages', or any of their components from a Next.JS app, you must use `next-transpile-modules` in the `next.config.js` file. This looks like:
|
||||
|
||||
```tsx
|
||||
// next.config.js
|
||||
const withTM = require('next-transpile-modules')(['ui', 'common'])
|
||||
module.exports = withTM({})
|
||||
```
|
||||
|
||||
### Installing packages
|
||||
|
||||
Installing a package with NPM workspaces requires you to add the `-w` flag to tell NPM which workspace you want to install into.
|
||||
|
||||
The format is: `npm install <package name> -w=<workspace to install in>`.
|
||||
|
||||
For example:
|
||||
|
||||
- `npm install @supabase/ui -w common`: installs into `./packages/common`
|
||||
- `npm install @supabase/ui -w www`: installs into `./apps/www`
|
||||
|
||||
You do not need to install `devDependencies` in each workspace. These can all be installed in the root package.
|
||||
|
||||
### Development
|
||||
|
||||
`npm run dev`
|
||||
|
||||
---
|
||||
|
||||
## Community channels
|
||||
|
||||
@@ -8,15 +8,7 @@ export default function AuthProviders() {
|
||||
<div key={x.name} className="col-span-6">
|
||||
<ButtonCard to={x.href} title={x.name}>
|
||||
<div className="px-6 py-4">
|
||||
<div
|
||||
className=""
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
gap: 10,
|
||||
}}
|
||||
>
|
||||
{/* {x.logo && <img src={x.logo} alt={x.name} width="20" />} */}
|
||||
<div className="flex justify-between">
|
||||
<p className="mt-0">{x.name}</p>
|
||||
<p className="mt-0">
|
||||
{x.official ? (
|
||||
|
||||
@@ -62,7 +62,7 @@ const CodeBlock: FC<Props> = ({
|
||||
const showLineNumbers = hideLineNumbers || lang !== 'bash'
|
||||
|
||||
return (
|
||||
<div className="relative my-2">
|
||||
<div className="relative my-2 max-w-[90vw] md:max-w-none overflow-auto">
|
||||
{title && (
|
||||
<div className="rounded-t-md bg-scale-300 py-2 px-4 border-b border-scale-500 text-blue-1100 font-sans">
|
||||
{title.replace(/%20/g, ' ')}
|
||||
@@ -125,7 +125,7 @@ const CodeBlock: FC<Props> = ({
|
||||
icon={copied ? <IconCheck /> : <IconCopy />}
|
||||
onClick={() => handleCopy()}
|
||||
>
|
||||
{copied ? 'Copied' : 'Copy'}
|
||||
{copied ? 'Copied' : ''}
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function GithubCard({ title, description, href, stars, handle }) {
|
||||
return (
|
||||
<a className="h-full" href={href}>
|
||||
<div className="card__body">
|
||||
<h4 className="uppercase my-2 font-semibold">{title}</h4>
|
||||
<span className="text-sm">{description}</span>
|
||||
</div>
|
||||
<hr className="my-2" />
|
||||
<div className="flex justify-between py-2 text-xs">
|
||||
<div>@{handle}</div>
|
||||
<div>{stars} ★</div>
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
@@ -197,7 +197,7 @@ const NavBar: FC<Props> = ({ currentPage }) => {
|
||||
<div className="flex items-center justify-between space-x-6 bg-scale-300 border border-scale-700 pl-3 pr-1.5 py-1.5 rounded">
|
||||
<div className="flex items-center space-x-2">
|
||||
<IconSearch className="text-scale-1100" size={18} strokeWidth={2} />
|
||||
<p className="text-scale-800 text-sm">Search docs</p>
|
||||
<p className="text-scale-800 text-sm">Search</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-1">
|
||||
<div className="hidden text-scale-1200 md:flex items-center justify-center h-6 w-6 rounded bg-scale-500">
|
||||
|
||||
@@ -134,17 +134,21 @@ export const menuItems: NavMenu = {
|
||||
name: 'Deep Dive',
|
||||
url: undefined,
|
||||
items: [
|
||||
{ name: 'Part One: JWTs', url: '/learn/auth-deep-dive/jwts', items: [] },
|
||||
{
|
||||
name: 'Part Two: Row Level Security',
|
||||
url: '/learn/auth-deep-dive/row-level-security',
|
||||
name: 'Part One: JWTs',
|
||||
url: '/learn/auth-deep-dive/auth-deep-dive-jwts',
|
||||
items: [],
|
||||
},
|
||||
{ name: 'Part Three: Policies', url: '/learn/auth-deep-dive/policies', items: [] },
|
||||
{ name: 'Part Four: GoTrue', url: '/learn/auth-deep-dive/gotrue', items: [] },
|
||||
{
|
||||
name: 'Part Two: Row Level Security',
|
||||
url: '/learn/auth-deep-dive/auth-row-level-security',
|
||||
items: [],
|
||||
},
|
||||
{ name: 'Part Three: Policies', url: '/learn/auth-deep-dive/auth-policies', items: [] },
|
||||
{ name: 'Part Four: GoTrue', url: '/learn/auth-deep-dive/auth-gotrue', items: [] },
|
||||
{
|
||||
name: 'Part Five: Google OAuth',
|
||||
url: '/learn/auth-deep-dive/google-oauth',
|
||||
url: '/learn/auth-deep-dive/auth-google-oauth',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -43,7 +43,7 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => {
|
||||
return (
|
||||
<div
|
||||
className="dark:bg-scale-200 dark:border-scale-400 sidebar-width sticky top-16
|
||||
h-screen overflow-y-scroll border-r py-8 px-6 sidebar-menu-container hidden md:block "
|
||||
h-screen overflow-y-scroll border-r py-8 px-6 sidebar-menu-container hidden lg:block"
|
||||
>
|
||||
{isInReferencePages && (
|
||||
<>
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import Image from 'next/image'
|
||||
import React from 'react'
|
||||
import sponsors from '../data/sponsors.json'
|
||||
|
||||
export default function Sponsors() {
|
||||
const tiers = [
|
||||
{
|
||||
tier_name: '$5,000 a month',
|
||||
heading: 'Enterprise: $5,000 per month',
|
||||
transactions: sponsors.filter((x) => x.tier == '$5,000 a month'),
|
||||
},
|
||||
{
|
||||
tier_name: '$2,500 a month',
|
||||
heading: 'Agency: $2,500 per month',
|
||||
transactions: sponsors.filter((x) => x.tier == '$2,500 a month'),
|
||||
},
|
||||
{
|
||||
tier_name: '$1,000 a month',
|
||||
heading: 'Startup: $1,000 per month',
|
||||
transactions: sponsors.filter((x) => x.tier == '$1,000 a month'),
|
||||
},
|
||||
{
|
||||
tier_name: '$49 a month',
|
||||
heading: 'Evangelist: $49 per month',
|
||||
transactions: sponsors.filter((x) => x.tier == '$49 a month'),
|
||||
},
|
||||
{
|
||||
tier_name: '$19 a month',
|
||||
heading: 'Supporter: $19 per month',
|
||||
transactions: sponsors.filter((x) => x.tier == '$19 a month'),
|
||||
},
|
||||
{
|
||||
tier_name: '$5 a month',
|
||||
heading: 'Contributor: $5 per month',
|
||||
transactions: sponsors.filter((x) => x.tier == '$5 a month'),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div>
|
||||
{tiers.map(
|
||||
(t, index) =>
|
||||
!!t.transactions.length && (
|
||||
<div key={index}>
|
||||
<h4 className="font-bold">{t.heading}</h4>
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-6 pl-4">
|
||||
{t.transactions.map((x, index) => (
|
||||
<div className="" key={index}>
|
||||
<a
|
||||
className="flex items-center gap-4 shadow-none shrink-0"
|
||||
href={`https://github.com/${x.sponsor}`}
|
||||
>
|
||||
<Image
|
||||
alt="Sponsor"
|
||||
className="mt-4 mb-4 rounded-full w-12 h-12"
|
||||
width={45}
|
||||
height={45}
|
||||
src={`https://github.com/${x.sponsor}.png`}
|
||||
/>
|
||||
<div className="flex items-center flex-col justify-center text-center">
|
||||
<h5 className="avatar__name">{x.sponsor}</h5>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,60 +1,96 @@
|
||||
[
|
||||
{
|
||||
"title": "Company",
|
||||
"title": "Product",
|
||||
"links": [
|
||||
{
|
||||
"text": "Blog",
|
||||
"url": "/blog"
|
||||
"text": "Database",
|
||||
"url": "/database"
|
||||
},
|
||||
{
|
||||
"text": "Open Source",
|
||||
"url": "/oss"
|
||||
"text": "Auth",
|
||||
"url": "/auth"
|
||||
},
|
||||
{
|
||||
"text": "Humans.txt",
|
||||
"url": "/humans.txt"
|
||||
"text": "Functions",
|
||||
"url": "/edge-functions"
|
||||
},
|
||||
{
|
||||
"text": "Lawyers.txt",
|
||||
"url": "/lawyers.txt"
|
||||
"text": "Realtime",
|
||||
"url": "/realtime"
|
||||
},
|
||||
{
|
||||
"text": "Storage",
|
||||
"url": "/storage"
|
||||
},
|
||||
{
|
||||
"text": "Pricing",
|
||||
"url": "/pricing"
|
||||
},
|
||||
{
|
||||
"text": "Beta",
|
||||
"url": "/beta"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Resources",
|
||||
"links": [
|
||||
{
|
||||
"text": "Brand Assets",
|
||||
"url": "/brand-assets"
|
||||
},
|
||||
{
|
||||
"text": "Docs",
|
||||
"url": "/docs/"
|
||||
},
|
||||
{
|
||||
"text": "Pricing",
|
||||
"url": "/pricing"
|
||||
},
|
||||
{
|
||||
"text": "Support",
|
||||
"url": "/docs/support"
|
||||
"url": "/support"
|
||||
},
|
||||
{
|
||||
"text": "System Status",
|
||||
"url": "https://status.supabase.com/"
|
||||
},
|
||||
{
|
||||
"text": "Integrations",
|
||||
"url": "/partners/integrations"
|
||||
},
|
||||
{
|
||||
"text": "Experts",
|
||||
"url": "/partners/experts"
|
||||
},
|
||||
{
|
||||
"text": "Brand Assets / Logos",
|
||||
"url": "/brand-assets"
|
||||
},
|
||||
{
|
||||
"text": "DPA",
|
||||
"url": "/legal/dpa"
|
||||
},
|
||||
{
|
||||
"text": "SOC2",
|
||||
"url": "/legal/soc2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Community",
|
||||
"title": "Developers",
|
||||
"links": [
|
||||
{
|
||||
"text": "GitHub",
|
||||
"url": "https://github.com/supabase/supabase"
|
||||
"text": "Documentation",
|
||||
"url": "/docs"
|
||||
},
|
||||
{
|
||||
"text": "Twitter",
|
||||
"url": "https://twitter.com/supabase"
|
||||
"text": "API Reference",
|
||||
"url": "/docs/reference"
|
||||
},
|
||||
{
|
||||
"text": "Changelog",
|
||||
"url": "/changelog"
|
||||
},
|
||||
{
|
||||
"text": "Contributing",
|
||||
"url": "/docs/handbook/contributing"
|
||||
},
|
||||
{
|
||||
"text": "Open Source",
|
||||
"url": "/oss"
|
||||
},
|
||||
{
|
||||
"text": "SupaSquad",
|
||||
"url": "/docs/handbook/supasquad"
|
||||
},
|
||||
{
|
||||
"text": "DevTo",
|
||||
@@ -63,23 +99,51 @@
|
||||
{
|
||||
"text": "RSS",
|
||||
"url": "/rss.xml"
|
||||
},
|
||||
{
|
||||
"text": "Discord",
|
||||
"url": "https://discord.supabase.com/"
|
||||
},
|
||||
{
|
||||
"text": "Youtube",
|
||||
"url": "https://youtube.com/c/supabase"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Beta",
|
||||
"title": "Company",
|
||||
"links": [
|
||||
{
|
||||
"text": "Join our beta",
|
||||
"url": "https://app.supabase.com/"
|
||||
"text": "Blog",
|
||||
"url": "/blog"
|
||||
},
|
||||
{
|
||||
"text": "Careers",
|
||||
"url": "https://about.supabase.com/careers"
|
||||
},
|
||||
{
|
||||
"text": "Company",
|
||||
"url": "/company"
|
||||
},
|
||||
{
|
||||
"text": "Terms of Service",
|
||||
"url": "/docs/company/terms"
|
||||
},
|
||||
{
|
||||
"text": "Privacy Policy",
|
||||
"url": "/docs/company/privacy"
|
||||
},
|
||||
{
|
||||
"text": "Acceptable Use Policy",
|
||||
"url": "/docs/company/aup"
|
||||
},
|
||||
{
|
||||
"text": "Service Level Agreement",
|
||||
"url": "/docs/company/sla"
|
||||
},
|
||||
{
|
||||
"text": "Humans.txt",
|
||||
"url": "/humans.txt"
|
||||
},
|
||||
{
|
||||
"text": "Lawyers.txt",
|
||||
"url": "/lawyers.txt"
|
||||
},
|
||||
{
|
||||
"text": "Security.txt",
|
||||
"url": "/.well-known/security.txt"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
|
||||
## Client-side data fetching with RLS
|
||||
|
||||
For [row level security](https://supabase.com/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to use the `supabaseClient` from the `useSupabaseClient` hook and only run your query once the user is defined client-side in the `useUser()` hook:
|
||||
For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to use the `supabaseClient` from the `useSupabaseClient` hook and only run your query once the user is defined client-side in the `useUser()` hook:
|
||||
|
||||
```jsx lines=10-17
|
||||
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
|
||||
@@ -263,7 +263,7 @@ export const getServerSideProps = async (ctx) => {
|
||||
|
||||
## Server-side data fetching with RLS
|
||||
|
||||
You can use the server supabase client to run [row level security](https://supabase.com/docs/learn/auth-deep-dive/auth-row-level-security) authenticated queries server-side:
|
||||
You can use the server supabase client to run [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) authenticated queries server-side:
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
@@ -779,44 +779,44 @@ export const config = {
|
||||
|
||||
### Migrating to v0.4.X and supabase-js v2
|
||||
|
||||
- With the update to `supabase-js` v2 the `auth` API routes are no longer required, therefore you can go ahead and delete your `auth` directory under the `/pages/api/` directory. Please refer to the [v2 migration guide](https://supabase.com/docs/reference/javascript/upgrade-guide) for the full set of changes within supabase-js.
|
||||
With the update to `supabase-js` v2 the `auth` API routes are no longer required, therefore you can go ahead and delete your `auth` directory under the `/pages/api/` directory. Please refer to the [v2 migration guide](/docs/reference/javascript/upgrade-guide) for the full set of changes within supabase-js.
|
||||
|
||||
- The `/api/auth/logout` API route has been removed, please use the `signout` method instead:
|
||||
The `/api/auth/logout` API route has been removed, please use the `signout` method instead:
|
||||
|
||||
```jsx
|
||||
<button
|
||||
onClick={async () => {
|
||||
await supabaseClient.auth.signOut()
|
||||
router.push('/')
|
||||
}}
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
```
|
||||
```jsx
|
||||
<button
|
||||
onClick={async () => {
|
||||
await supabaseClient.auth.signOut()
|
||||
router.push('/')
|
||||
}}
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
```
|
||||
|
||||
- The `supabaseClient` and `supabaseServerClient` have been removed in favor of the `createBrowserSupabaseClient` and `createServerSupabaseClient` methods. This allows you to provide the CLI-generated types to the client:
|
||||
The `supabaseClient` and `supabaseServerClient` have been removed in favor of the `createBrowserSupabaseClient` and `createServerSupabaseClient` methods. This allows you to provide the CLI-generated types to the client:
|
||||
|
||||
```tsx
|
||||
// client-side
|
||||
import type { Database } from 'types_db'
|
||||
const [supabaseClient] = useState(() => createBrowserSupabaseClient<Database>())
|
||||
```tsx
|
||||
// client-side
|
||||
import type { Database } from 'types_db'
|
||||
const [supabaseClient] = useState(() => createBrowserSupabaseClient<Database>())
|
||||
|
||||
// server-side API route
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { Database } from 'types_db'
|
||||
// server-side API route
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
import type { Database } from 'types_db'
|
||||
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const supabaseServerClient = createServerSupabaseClient<Database>({
|
||||
req,
|
||||
res,
|
||||
})
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabaseServerClient.auth.getUser()
|
||||
export default async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const supabaseServerClient = createServerSupabaseClient<Database>({
|
||||
req,
|
||||
res,
|
||||
})
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabaseServerClient.auth.getUser()
|
||||
|
||||
res.status(200).json({ name: user?.name ?? '' })
|
||||
}
|
||||
```
|
||||
res.status(200).json({ name: user?.name ?? '' })
|
||||
}
|
||||
```
|
||||
|
||||
- The `UserProvider` has been replaced by the `SessionContextProvider`. Make sure to wrap your `pages/_app.js` componenent with the `SessionContextProvider`. Then, throughout your application you can use the `useSessionContext` hook to get the `session` and the `useSupabaseClient` hook to get an authenticated `supabaseClient`.
|
||||
- The `useUser` hook now returns the `user` object or `null`.
|
||||
|
||||
@@ -438,7 +438,7 @@ Given this Remix `<Form />` component.
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
Any of the [supported authentication strategies from `supabase-js`](https://supabase.com/docs/reference/javascript/auth-signup) will work server-side. This is how you would handle simple `email` and `password` auth.
|
||||
Any of the [supported authentication strategies from `supabase-js`](/docs/reference/javascript/auth-signup) will work server-side. This is how you would handle simple `email` and `password` auth.
|
||||
|
||||
```jsx
|
||||
export const action = async ({ request }) => {
|
||||
@@ -470,7 +470,7 @@ export const action = async ({ request }) => {
|
||||
</TabPanel>
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
Any of the [supported authentication strategies from `supabase-js`](https://supabase.com/docs/reference/javascript/auth-signup) will work server-side. This is how you would handle simple `email` and `password` auth.
|
||||
Any of the [supported authentication strategies from `supabase-js`](/docs/reference/javascript/auth-signup) will work server-side. This is how you would handle simple `email` and `password` auth.
|
||||
|
||||
```jsx
|
||||
export const action: ActionFunction = async ({
|
||||
@@ -516,7 +516,7 @@ export const action: ActionFunction = async ({
|
||||
>
|
||||
<TabPanel id="js" label="JavaScript">
|
||||
|
||||
Any of the [supported authentication strategies from `supabase-js`](https://supabase.com/docs/reference/javascript/auth-signinwithpassword) will work server-side. This is how you would handle simple `email` and `password` auth.
|
||||
Any of the [supported authentication strategies from `supabase-js`](/docs/reference/javascript/auth-signinwithpassword) will work server-side. This is how you would handle simple `email` and `password` auth.
|
||||
|
||||
```jsx
|
||||
export const action = async ({ request }) => {
|
||||
@@ -548,7 +548,7 @@ export const action = async ({ request }) => {
|
||||
</TabPanel>
|
||||
<TabPanel id="ts" label="TypeScript">
|
||||
|
||||
Any of the [supported authentication strategies from `supabase-js`](https://supabase.com/docs/reference/javascript/auth-signinwithpassword) will work server-side. This is how you would handle simple `email` and `password` auth.
|
||||
Any of the [supported authentication strategies from `supabase-js`](/docs/reference/javascript/auth-signinwithpassword) will work server-side. This is how you would handle simple `email` and `password` auth.
|
||||
|
||||
```jsx
|
||||
export const action: ActionFunction = async ({
|
||||
|
||||
@@ -208,7 +208,7 @@ You can now determine if a user is authenticated on the client-side by checking
|
||||
|
||||
## Client-side data fetching with RLS
|
||||
|
||||
For [row level security](https://supabase.com/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to import the `{ supabaseClient }` from `$lib/db` and only run your query once the user is defined client-side in `$page.data.session`:
|
||||
For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to import the `{ supabaseClient }` from `$lib/db` and only run your query once the user is defined client-side in `$page.data.session`:
|
||||
|
||||
```html
|
||||
<script>
|
||||
@@ -246,7 +246,7 @@ For [row level security](https://supabase.com/docs/learn/auth-deep-dive/auth-row
|
||||
<pre>{JSON.stringify(user, null, 2)}</pre>
|
||||
```
|
||||
|
||||
For [row level security](https://supabase.com/docs/learn/auth-deep-dive/auth-row-level-security) to work in a server environment, you need to use the `withAuth` helper to check if the user is authenticated. The helper extends the event with `session` and `getSupabaseClient()`:
|
||||
For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work in a server environment, you need to use the `withAuth` helper to check if the user is authenticated. The helper extends the event with `session` and `getSupabaseClient()`:
|
||||
|
||||
```ts title=src/routes/profile/+page.ts
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
@@ -82,6 +82,12 @@ For example:
|
||||
psql 'postgresql://postgres:postgres@localhost:54322/postgres'
|
||||
```
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
To access the database from an edge function in your local Supabase setup, replace `localhost` with `host.docker.internal`.
|
||||
|
||||
</Admonition>
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="kong" label="API Gateway">
|
||||
|
||||
@@ -91,7 +97,7 @@ http://localhost:54321
|
||||
```
|
||||
|
||||
If you are accessing these services without the client libraries, you may need to pass the client keys as an `Authorization` header.
|
||||
Learn more about JWT headers in our [Resources](/learn/auth-deep-dive/auth-deep-dive-jwts).
|
||||
Learn more about [JWT headers](/docs/learn/auth-deep-dive/auth-deep-dive-jwts).
|
||||
|
||||
```sh
|
||||
curl 'http://localhost:54321/rest/v1/' \
|
||||
@@ -104,15 +110,15 @@ http://localhost:54321/storage/v1/ # Storage
|
||||
http://localhost:54321/auth/v1/ # Auth (GoTrue)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
To access the database from an edge function in your local Supabase setup, replace `localhost` with `host.docker.internal`.
|
||||
`<anon key>` is provided when you run the command `supabase start`.
|
||||
|
||||
</Admonition>
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Database migrations
|
||||
|
||||
Database changes are managed through "migrations." Database migrations are a common way of tracking changes to your database over time.
|
||||
|
||||
@@ -66,5 +66,5 @@ Result: PASS
|
||||
|
||||
## More resource
|
||||
|
||||
- [pgTAP extension](https://supabase.com/docs/guides/database/extensions/pgtap)
|
||||
- [pgTAP extension](/docs/guides/database/extensions/pgtap)
|
||||
- Official [pgTAP documentation](https://pgtap.org/)
|
||||
@@ -115,8 +115,8 @@ AUTH0_ISSUER_BASE_URL=https://<name-of-your-tenant>.<region-you-selected>.auth0.
|
||||
AUTH0_CLIENT_ID=get-from-auth0-dashboard
|
||||
AUTH0_CLIENT_SECRET=get-from-auth0-dashboard
|
||||
NEXT_PUBLIC_SUPABASE_URL=get-from-supabase-dashboard
|
||||
NEXT_PUBLIC_SUPABASE_KEY=get-from-supabase-dashboard
|
||||
SUPABASE_SIGNING_SECRET=get-from-supabase-dashboard
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=get-from-supabase-dashboard
|
||||
SUPABASE_JWT_SECRET=get-from-supabase-dashboard
|
||||
```
|
||||
|
||||
> Note: Auth0 values can be found under `Settings > Basic Information` for your application.
|
||||
@@ -239,7 +239,7 @@ const afterCallback = async (req, res, session) => {
|
||||
exp: Math.floor(Date.now() / 1000) + 60 * 60,
|
||||
}
|
||||
|
||||
session.user.accessToken = jwt.sign(payload, process.env.SUPABASE_SIGNING_SECRET)
|
||||
session.user.accessToken = jwt.sign(payload, process.env.SUPABASE_JWT_SECRET)
|
||||
|
||||
return session
|
||||
}
|
||||
@@ -273,17 +273,22 @@ Create a new file called `utils/supabase.js` and add the following:
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
const getSupabase = (access_token) => {
|
||||
const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_KEY
|
||||
)
|
||||
const options = {}
|
||||
|
||||
if (access_token) {
|
||||
supabase.auth.session = () => ({
|
||||
access_token,
|
||||
})
|
||||
options.global = {
|
||||
headers: {
|
||||
Authorization: `Bearer ${access_token}`,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
||||
options
|
||||
)
|
||||
|
||||
return supabase
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ After the key is added, click the **Apply Changes** button to save your template
|
||||
|
||||
## Step 3: Configure client
|
||||
|
||||
The next step is to configure your client. Supabase provides an official [JavaScript/TypeScript client library](https://github.com/supabase/supabase-js) and there are [libraries in other languages](https://supabase.com/docs/reference/javascript/installing) built by the community.
|
||||
The next step is to configure your client. Supabase provides an official [JavaScript/TypeScript client library](https://github.com/supabase/supabase-js) and there are [libraries in other languages](/docs/reference/javascript/installing) built by the community.
|
||||
|
||||
This guide will use a Next.js project with the JS client as an example, but the mechanism of setting the authentication token should be similar with other libraries and frameworks.
|
||||
|
||||
@@ -64,7 +64,7 @@ To get the ones needed for Supabase, navigate to the same Settings > API page as
|
||||
|
||||

|
||||
|
||||
**Note**: It is recommended that you enable [Row Level Security](https://supabase.com/docs/guides/auth/row-level-security) (RLS) for your database tables and configure access policies as needed.
|
||||
**Note**: It is recommended that you enable [Row Level Security](/docs/guides/auth/row-level-security) (RLS) for your database tables and configure access policies as needed.
|
||||
|
||||
After setting those three environment variables, you should be able to start up your application development server.
|
||||
|
||||
|
||||
@@ -16,13 +16,13 @@ Just so we are on the same page, here are some of the terms we use:
|
||||
- **Dashboard:** This is the friendly interface that you build on top of your Supabase database using Dashibase.
|
||||
- **Page:** Each dashboard can have multiple pages. Each page can have multiple blocks of text or tables.
|
||||
|
||||
Now that is out of the way, let's build our customer admin panel.
|
||||
Now that is out of the way, let's build our customer admin panel.
|
||||
|
||||
(Start your stopwatch!)
|
||||
|
||||
## Step 1. Connect Supabase to Dashibase
|
||||
|
||||
First, in Dashibase, go to “Data Sources” via the left sidebar and click on “Connect data source” in the upper-right corner.
|
||||
First, in Dashibase, go to “Data Sources” via the left sidebar and click on “Connect data source” in the upper-right corner.
|
||||
|
||||
Note: We do not download or store any of your database data.
|
||||
|
||||
@@ -36,7 +36,7 @@ Enter your database credentials, which you can find in Supabase under Settings >
|
||||
|
||||
Once you have connected your database, you can also control what your team can see and edit in your database via Dashibase. There are usually certain data that you don’t want others to edit or even see (e.g. address, email, identification number).
|
||||
|
||||
Note: If you prefer to restrict access to your database _before_ you connect to Dashibase for security reasons, you can [create a restricted user](https://aws.amazon.com/blogs/database/managing-postgresql-users-and-roles/) via [Supabase's SQL Editor](https://supabase.com/docs/guides/database#the-sql-editor). The slight advantage of doing this in Dashibase is that there is a GUI, which makes it easier to set up the access control (but we understand some developers might find it more secure to restrict the access themselves).
|
||||
Note: If you prefer to restrict access to your database _before_ you connect to Dashibase for security reasons, you can [create a restricted user](https://aws.amazon.com/blogs/database/managing-postgresql-users-and-roles/) via [Supabase's SQL Editor](/docs/guides/database#the-sql-editor). The slight advantage of doing this in Dashibase is that there is a GUI, which makes it easier to set up the access control (but we understand some developers might find it more secure to restrict the access themselves).
|
||||
|
||||

|
||||
|
||||
@@ -50,7 +50,7 @@ Once you are happy with the data access control, you can decide who in your orga
|
||||
|
||||
## Step 3. Build your dashboard
|
||||
|
||||
To create a dashboard, go to “Dashboards” via the left sidebar in Dashibase and click on “Create dashboard” in the upper-right corner.
|
||||
To create a dashboard, go to “Dashboards” via the left sidebar in Dashibase and click on “Create dashboard” in the upper-right corner.
|
||||
|
||||
Here’s where the fun begins. There are many ways to build your dashboards in Dashibase using features such as markdown formatting and slash command.
|
||||
|
||||
@@ -62,11 +62,11 @@ Using our Notion-like UI, you can quickly build out your dashboard through typin
|
||||
|
||||

|
||||
|
||||
If you have set up [foreign keys in your Supabase database](https://supabase.com/docs/guides/database/tables#joining-tables-with-foreign-keys), you can easily show data from multiple tables on the table in your Dashibase dashboard. Simply click on the "Columns" button beside the "Filter" button and select the columns you want to show or hide. For this example, we want to show the customers' country and plan price, which are in separate `countries` and `plans` tables.
|
||||
If you have set up [foreign keys in your Supabase database](/docs/guides/database/tables#joining-tables-with-foreign-keys), you can easily show data from multiple tables on the table in your Dashibase dashboard. Simply click on the "Columns" button beside the "Filter" button and select the columns you want to show or hide. For this example, we want to show the customers' country and plan price, which are in separate `countries` and `plans` tables.
|
||||
|
||||

|
||||
|
||||
For all the tables in your dashboard, you can rename the column headers, add filter and sort, adjust the column width, and more.
|
||||
For all the tables in your dashboard, you can rename the column headers, add filter and sort, adjust the column width, and more.
|
||||
|
||||
You can also add text to provide context or instructions for the rest of your team as they are using your dashboard. This saves you the trouble of having your dashboard and documentation in separate places.
|
||||
|
||||
@@ -87,7 +87,7 @@ Click on the “Share” button in the upper-right corner of your dashboard to s
|
||||
|
||||

|
||||
|
||||
Now your team can use this customer admin panel to find and update customer information easily!
|
||||
Now your team can use this customer admin panel to find and update customer information easily!
|
||||
|
||||
P.S. How long did we take? :)
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ Let's dive into how we actually set up and link these two platforms to create a
|
||||
|
||||
## Create a Supabase Project
|
||||
|
||||
As mentioned, while you can [deploy Supabase locally](https://supabase.com/docs/guides/local-development). For the purpose of this guide, we'll use Supabase Cloud:
|
||||
As mentioned, while you can [deploy Supabase locally](/docs/guides/cli/local-development). For the purpose of this guide, we'll use Supabase Cloud:
|
||||
|
||||
1. Create a **Supabase** account by signing in with GitHub.
|
||||
2. Give your organization a name (this can be changed later).
|
||||
|
||||
@@ -6,10 +6,10 @@ description: 'Get started with Supabase and Plasmic, an open-source framework fo
|
||||
|
||||
In this guide, we will show you how to build a crowd-sourced Pokemon Pokedex, by connecting **Supabase**, an open source Firebase backend alternative, with **Plasmic**, a visual builder for the web. While many users leverage Plasmic to quickly launch and iterate on landing pages, in this tutorial we’ll show just how powerful Plasmic can be as a general-purpose visual builder for React, which can be used to design and implement fully featured read-write applications.
|
||||
|
||||
You can play with the live demo here:
|
||||
You can play with the live demo here:
|
||||
[https://plasmic-supabase-demo.vercel.app/](https://plasmic-supabase-demo.vercel.app/)
|
||||
|
||||
You can also take a look at the Plasmic project here:
|
||||
You can also take a look at the Plasmic project here:
|
||||
[https://studio.plasmic.app/projects/66RKaSPCwKxYjCfXWHCxn6](https://studio.plasmic.app/projects/66RKaSPCwKxYjCfXWHCxn6)
|
||||
|
||||
> You’ll need to enable 3rd-party cookies in your browser for the project to properly load.
|
||||
@@ -35,7 +35,7 @@ At a high level,
|
||||
- `name`, `description`, `imageUrl`: This will store the name, description, and imageUrl for each Pokemon.
|
||||
- `inserted_at` : This will be an automatically populated column, set to when the row was first inserted.
|
||||
|
||||
> Note: In this tutorial we’ve turned off “[Row Level Security (RLS)](https://supabase.com/docs/guides/auth/row-level-security)”. In practice, you will want to create policies that restrict who gets to create, edit, and delete posts. By turning this off, any user can modify the database without restrictions.
|
||||
> Note: In this tutorial we’ve turned off “[Row Level Security (RLS)](/docs/guides/auth/row-level-security)”. In practice, you will want to create policies that restrict who gets to create, edit, and delete posts. By turning this off, any user can modify the database without restrictions.
|
||||
|
||||

|
||||
|
||||
@@ -77,7 +77,7 @@ If you pre-populated the database in Step 1. you should see the following homepa
|
||||
|
||||
Now let’s try to make some additions! The code base is currently configured to a read-only copy of the Plasmic project. Let’s make an editable copy first.
|
||||
|
||||
Open the default starter Plasmic project here:
|
||||
Open the default starter Plasmic project here:
|
||||
[https://studio.plasmic.app/projects/66RKaSPCwKxYjCfXWHCxn6](https://studio.plasmic.app/projects/66RKaSPCwKxYjCfXWHCxn6)
|
||||
|
||||

|
||||
|
||||
@@ -62,7 +62,7 @@ To ensure the right users can access the synced schema Sequin manages, you'll ne
|
||||
|
||||
## Step 3: Configure the Supabase Client
|
||||
|
||||
Finally, you'll need to define a new [Supabase client](https://supabase.com/docs/reference/javascript/initializing#api-schemas) in your application to access your synced schema. In the file where you initialized your Supabase client, define a new client with a `schema` parameter:
|
||||
Finally, you'll need to define a new [Supabase client](/docs/reference/javascript/initializing#api-schemas) in your application to access your synced schema. In the file where you initialized your Supabase client, define a new client with a `schema` parameter:
|
||||
|
||||
```javascript
|
||||
export const supabase_schema = createClient(
|
||||
|
||||
@@ -86,5 +86,5 @@ For more advanced migrations, including the use of a middleware server component
|
||||
## Resources
|
||||
|
||||
- [Supabase vs Firebase](https://supabase.com/alternatives/supabase-vs-firebase)
|
||||
- [Firestore Data Migration](https://supabase.com/docs/guides/migrations/firestore-data)
|
||||
- [Firestore Storage Migration](https://supabase.com/docs/guides/migrations/firebase-storage)
|
||||
- [Firestore Data Migration](/docs/guides/migrations/firestore-data)
|
||||
- [Firestore Storage Migration](/docs/guides/migrations/firebase-storage)
|
||||
@@ -13,9 +13,11 @@ Supabase provides several [tools](https://github.com/supabase-community/firebase
|
||||
## Set up the migration tool {#set-up-migration-tool}
|
||||
|
||||
1. Clone the [firebase-to-supabase](https://github.com/supabase-community/firebase-to-supabase) repository:
|
||||
```bash
|
||||
git clone https://github.com/supabase-community/firebase-to-supabase.git
|
||||
```
|
||||
|
||||
```bash
|
||||
git clone https://github.com/supabase-community/firebase-to-supabase.git
|
||||
```
|
||||
|
||||
1. In the `/storage` directory, rename [supabase-keys-sample.js](https://github.com/supabase-community/firebase-to-supabase/blob/main/storage/supabase-keys-sample.js) to `supabase-keys.js`.
|
||||
1. Go to your Supabase project's [API settings](https://app.supabase.com/project/_/settings/api) in the Dashboard.
|
||||
1. Copy the **Project URL** and update the `SUPABASE_URL` value in `supabase-keys.js`.
|
||||
@@ -60,5 +62,5 @@ If the bucket doesn't exist, it's created as a `non-public` bucket. You must set
|
||||
## Resources
|
||||
|
||||
- [Supabase vs Firebase](https://supabase.com/alternatives/supabase-vs-firebase)
|
||||
- [Firestore Data Migration](https://supabase.com/docs/guides/migrations/firestore-data)
|
||||
- [Firebase Auth Migration](https://supabase.com/docs/guides/migrations/firebase-auth)
|
||||
- [Firestore Data Migration](/docs/guides/migrations/firestore-data)
|
||||
- [Firebase Auth Migration](/docs/guides/migrations/firebase-auth)
|
||||
@@ -206,5 +206,5 @@ The result is two separate JSON files:
|
||||
## Resources
|
||||
|
||||
- [Supabase vs Firebase](https://supabase.com/alternatives/supabase-vs-firebase)
|
||||
- [Firestore Storage Migration](https://supabase.com/docs/guides/migrations/firebase-storage)
|
||||
- [Firebase Auth Migration](https://supabase.com/docs/guides/migrations/firebase-auth)
|
||||
- [Firestore Storage Migration](/docs/guides/migrations/firebase-storage)
|
||||
- [Firebase Auth Migration](/docs/guides/migrations/firebase-auth)
|
||||
@@ -19,6 +19,7 @@ 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/hosting/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
|
||||
|
||||
@@ -17,9 +17,9 @@ Aside from performance, CDNs also help with security and availability by mitigat
|
||||
|
||||
## Cache duration
|
||||
|
||||
By default, assets are cached both in the CDN and in the user’s browser for 1 hour. After this, the CDN nodes ping the storage server to see if an object has been updated.
|
||||
By default, assets are cached both in the CDN and in the user's browser for 1 hour. After this, the CDN nodes ping the storage server to see if an object has been updated.
|
||||
|
||||
You can modify this cache time when you are [uploading](https://supabase.com/docs/reference/javascript/storage-from-upload) or [updating](https://supabase.com/docs/reference/javascript/storage-from-update) an object by modifying the `cacheControl` parameter.
|
||||
You can modify this cache time when you are [uploading](/docs/reference/javascript/storage-from-upload) or [updating](/docs/reference/javascript/storage-from-update) an object by modifying the `cacheControl` parameter.
|
||||
|
||||
If you expect the object to not change at a given URL, setting a longer cache duration is preferable.
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ You can find the full contents of this file [here](https://raw.githubusercontent
|
||||
|
||||
Next.js is a highly versatile framework offering pre-rendering at build time (SSG), server-side rendering at request time (SSR), API routes, and middleware edge-functions.
|
||||
|
||||
It can be challenging to authenticate your users in all these different environments, that's why we've created the [Supabase Auth Helpers](https://supabase.com/docs/guides/auth/auth-helpers/nextjs) to make user management and data fetching within Next.js as easy as possible.
|
||||
It can be challenging to authenticate your users in all these different environments, that's why we've created the [Supabase Auth Helpers](/docs/guides/auth/auth-helpers/nextjs) to make user management and data fetching within Next.js as easy as possible.
|
||||
|
||||
Install the auth helpers for React and Next.js
|
||||
|
||||
@@ -160,7 +160,7 @@ See the [Auth Helpers docs](/docs/guides/auth/auth-helpers/nextjs#usage-with-typ
|
||||
|
||||
#### Supabase Auth UI
|
||||
|
||||
We can use the [Supabase Auth UI](https://supabase.com/docs/guides/auth/auth-helpers/auth-ui) a pre-built React component for authenticating users via OAuth, email, and magic links.
|
||||
We can use the [Supabase Auth UI](/docs/guides/auth/auth-helpers/auth-ui) a pre-built React component for authenticating users via OAuth, email, and magic links.
|
||||
|
||||
Install the Supabase Auth UI for React
|
||||
|
||||
@@ -728,8 +728,8 @@ return (
|
||||
At this stage you have a fully functional application!
|
||||
|
||||
- See the complete [example on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-ts-user-management) and deploy it to Vercel.
|
||||
- Explore the [pre-built Auth UI for React](https://supabase.com/docs/guides/auth/auth-helpers/auth-ui).
|
||||
- Explore the [Auth Helpers for Next.js](https://supabase.com/docs/guides/auth/auth-helpers/nextjs).
|
||||
- Explore the [pre-built Auth UI for React](/docs/guides/auth/auth-helpers/auth-ui).
|
||||
- Explore the [Auth Helpers for Next.js](/docs/guides/auth/auth-helpers/nextjs).
|
||||
- Explore the [Supabase Cache Helpers](https://github.com/psteinroe/supabase-cache-helpers).
|
||||
- See the [Next.js Subscription Payments Starter](https://github.com/vercel/nextjs-subscription-payments) template on GitHub.
|
||||
- Got a question? [Ask here](https://github.com/supabase/supabase/discussions).
|
||||
|
||||
@@ -19,4 +19,4 @@ GoTrue is an open-source API written in Golang, that acts as a self-standing API
|
||||
|
||||
- [Source Code](https://github.com/supabase/gotrue)
|
||||
- [Known bugs and issues](https://github.com/supabase/gotrue/issues)
|
||||
- [Auth Guides](https://supabase.com/docs/guides/auth)
|
||||
- [Auth Guides](/docs/guides/auth)
|
||||
@@ -10,13 +10,13 @@ hide_table_of_contents: true
|
||||
The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.
|
||||
The CLI is still under development, but it contains all the functionality for working with your Supabase projects and the Supabase Platform.
|
||||
|
||||
- Run Supabase locally: [`supabase start`](https://supabase.com/docs/reference/cli/usage#supabase-start)
|
||||
- Manage database migrations: [`supabase migration`](https://supabase.com/docs/reference/cli/usage#supabase-migration)
|
||||
- CI/CD for releasing to production: [`supabase db push`](https://supabase.com/docs/reference/cli/usage#supabase-db-push)
|
||||
- Manage your Supabase projects: [`supabase projects`](https://supabase.com/docs/reference/cli/usage#supabase-projects)
|
||||
- Generate types directly from your database schema: [`supabase gen types`](https://supabase.com/docs/reference/cli/usage#supabase-gen)
|
||||
- Run Supabase locally: [`supabase start`](/docs/reference/cli/usage#supabase-start)
|
||||
- Manage database migrations: [`supabase migration`](/docs/reference/cli/usage#supabase-migration)
|
||||
- CI/CD for releasing to production: [`supabase db push`](/docs/reference/cli/usage#supabase-db-push)
|
||||
- Manage your Supabase projects: [`supabase projects`](/docs/reference/cli/usage#supabase-projects)
|
||||
- Generate types directly from your database schema: [`supabase gen types`](/docs/reference/cli/usage#supabase-gen)
|
||||
- A [community-supported GitHub Action](https://github.com/lyqht/generate-supabase-db-types-github-action) to generate TypeScript types
|
||||
- Shell autocomplete: [`supabase completion`](https://supabase.com/docs/reference/cli/usage#supabase-completion)
|
||||
- Shell autocomplete: [`supabase completion`](/docs/reference/cli/usage#supabase-completion)
|
||||
- A [community-supported Fig autocomplete spec](https://fig.io/manual/supabase) for macOS terminal
|
||||
|
||||
## Additional Links
|
||||
|
||||
@@ -24,5 +24,5 @@ Read [this post](https://supabase.com/blog/supabase-storage) on why we decided t
|
||||
|
||||
- [Source Code](https://github.com/supabase/storage-api)
|
||||
- [Known bugs and issues](https://github.com/supabase/storage-js/issues)
|
||||
- [Storage Guides](https://supabase.com/docs/guides/storage)
|
||||
- [Storage Guides](/guides/storage)
|
||||
- [OpenAPI Docs](https://supabase.github.io/storage-api/)
|
||||
@@ -49,10 +49,10 @@ const Layout: FC<Props> = ({ meta, children, toc, menuItems, currentPage }) => {
|
||||
className={`${
|
||||
meta?.hide_table_of_contents || !hasTableOfContents
|
||||
? 'col-span-12 xl:col-start-2 xl:col-span-10 2xl:col-start-3 2xl:col-span-8'
|
||||
: 'col-span-12 md:col-span-9'
|
||||
} py-2 md:py-4 px-2 md:px-8`}
|
||||
: 'col-span-12 lg:col-span-9'
|
||||
} py-2 lg:py-4 px-2 lg:px-8 mx-auto`}
|
||||
>
|
||||
<article className="prose dark:prose-dark dark:bg-scale-200 width-full mt-8">
|
||||
<article className="doc-content-container prose dark:prose-dark dark:bg-scale-200 width-full mt-8 2xl:max-w-[880px] ">
|
||||
{children}
|
||||
</article>
|
||||
</div>
|
||||
@@ -60,7 +60,7 @@ const Layout: FC<Props> = ({ meta, children, toc, menuItems, currentPage }) => {
|
||||
<div
|
||||
className={[
|
||||
'border-scale-400 dark:bg-scale-200 table-of-contents-height border-l',
|
||||
'thin-scrollbar overflow-y-auto sticky hidden md:block md:col-span-3 px-2',
|
||||
'thin-scrollbar overflow-y-auto sticky hidden xl:block md:col-span-3 px-2',
|
||||
].join(' ')}
|
||||
>
|
||||
<TableOfContents toc={toc} />
|
||||
|
||||
@@ -46,6 +46,10 @@ const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
basePath: '/docs',
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
domains: ['avatars.githubusercontent.com', 'github.com', 'user-images.githubusercontent.com'],
|
||||
},
|
||||
}
|
||||
|
||||
// next.config.js
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"next-mdx-remote": "^3.0.8",
|
||||
"next-themes": "0.0.15",
|
||||
"next-transpile-modules": "^9.0.0",
|
||||
"octokit": "^2.0.10",
|
||||
"react": "17.0.2",
|
||||
"react-copy-to-clipboard": "^5.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Octokit } from '@octokit/core'
|
||||
import Layout from '~/layouts/Default'
|
||||
import maintainers from '../data/maintainers.json'
|
||||
import { menuItems } from '~/components/Navigation/Navigation.constants'
|
||||
import GithubCard from '../components/GithubCard'
|
||||
import Sponsors from '../components/Sponsors'
|
||||
import Image from 'next/image'
|
||||
|
||||
export default function Oss({ meta }) {
|
||||
const octokit = new Octokit()
|
||||
|
||||
const [activePill, setActivePill] = useState('All')
|
||||
const [repos, setRepos] = useState([])
|
||||
|
||||
const maintainerTags = maintainers
|
||||
.reduce((acc, x) => acc.concat(x.tags), []) // get all tags
|
||||
.filter((v, i, a) => a.indexOf(v) === i) // remove duplicates
|
||||
.sort((a, b) => a.localeCompare(b)) // alphabetical
|
||||
const maintainerPills = ['All'].concat(maintainerTags)
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchOctoData() {
|
||||
let res = await octokit.request('GET /orgs/{org}/repos', {
|
||||
org: 'supabase',
|
||||
type: 'public',
|
||||
per_page: 6,
|
||||
page: 1,
|
||||
})
|
||||
setRepos(
|
||||
res.data
|
||||
.filter((r) => !!r.stargazers_count)
|
||||
.sort((a, b) => b.stargazers_count - a.stargazers_count)
|
||||
)
|
||||
}
|
||||
fetchOctoData()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Layout meta={meta} menuItems={menuItems['docs']} currentPage="docs">
|
||||
<section className={'section-lg'}>
|
||||
<div className="container">
|
||||
<div className="flex items-center">
|
||||
<div className="col">
|
||||
<h1 className="mt-0">Open source</h1>
|
||||
<p className="">
|
||||
Supabase is an open source company, supporting existing open source tools and
|
||||
communities wherever possible.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={'section-lg'}>
|
||||
<div className="container">
|
||||
<h2 className="with-underline">Sponsors</h2>
|
||||
<Sponsors />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div className="">
|
||||
<h2>Community Maintainers</h2>
|
||||
|
||||
<div className="overflow-auto max-w-[300px] md:max-w-none">
|
||||
<ul className="flex gap-4 items-center p-0">
|
||||
{maintainerPills.map((x) => (
|
||||
<li
|
||||
key={x}
|
||||
className={`mx-4 rounded-sm inline-block p-2 cursor-pointer hover:text-brand-800 ${
|
||||
activePill == x ? 'bg-gray-200 dark:bg-gray-400 text-brand-800' : ''
|
||||
}`}
|
||||
onClick={() => setActivePill(x)}
|
||||
>
|
||||
{x}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="grid lg:grid-cols-2 gap-4">
|
||||
{maintainers
|
||||
.filter((x) => activePill == 'All' || x.tags.includes(activePill))
|
||||
.sort((a, b) => a.handle.localeCompare(b.handle))
|
||||
.map((x, idx) => (
|
||||
<div className="" key={idx}>
|
||||
<a
|
||||
className="shadow-none flex gap-4"
|
||||
href={`https://github.com/${x.handle}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<div className="grow bg-gray-200 dark:bg-gray-400 p-4">
|
||||
<div className="flex gap-4 shrink-0">
|
||||
<Image
|
||||
className="rounded-full my-0"
|
||||
width={50}
|
||||
height={50}
|
||||
layout="fixed"
|
||||
alt={x.handle}
|
||||
src={`https://github.com/${x.handle}.png`}
|
||||
/>
|
||||
<div className="">
|
||||
<h4 className="text-lg my-0">@{x.handle}</h4>
|
||||
<span className="text-sm">{x.description}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* OSS */}
|
||||
<section>
|
||||
<div>
|
||||
<h2>Repositories</h2>
|
||||
<div className="grid gap-4">
|
||||
{repos.length < 1 && <div></div>}
|
||||
{repos.length >= 1 &&
|
||||
repos.map((props, idx) => (
|
||||
<div className="bg-gray-200 dark:bg-gray-400 p-4" key={idx}>
|
||||
<GithubCard
|
||||
title={props.name}
|
||||
description={props.description}
|
||||
href={props.html_url}
|
||||
stars={props.stargazers_count}
|
||||
handle={props.full_name}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -55,7 +55,7 @@ article h1 {
|
||||
.docs-width {
|
||||
max-width: calc(100% - var(--sidebar-width));
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
@media screen and (max-width: 1024px) {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,6 @@ article h1 {
|
||||
|
||||
.width-full {
|
||||
width: 100% !important;
|
||||
max-width: max-content !important;
|
||||
}
|
||||
|
||||
.p:not(.prose *):not(.overwrite) {
|
||||
@@ -189,9 +188,7 @@ pre[class*='language-'] {
|
||||
// format <code> inside <p>
|
||||
h2 code,
|
||||
h3 code,
|
||||
h4 code,
|
||||
li code,
|
||||
p code {
|
||||
h4 code {
|
||||
word-break: keep-all !important;
|
||||
white-space: nowrap !important;
|
||||
padding: 0.1rem 0.35rem !important;
|
||||
@@ -220,8 +217,11 @@ a:has(code) {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
pre code {
|
||||
@media screen and (max-width: 768px) {
|
||||
white-space: normal !important;
|
||||
}
|
||||
// fix firefox issue with li wrapping
|
||||
.doc-content-container ul li div.relative {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.doc-content-container ul li a {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
# Developing Supabase Docs
|
||||
|
||||
1. [Development setup](#development-setup)
|
||||
- [Install dependencies](#install-dependencies)
|
||||
- [Fork the repository](#fork-the-repository)
|
||||
2. [Build Supabase](#build-supabase)
|
||||
- [Run the docs locally](#run-the-docs-locally)
|
||||
- [Editing files](#editing-files)
|
||||
3. [Create a pull request](#create-a-pull-request)
|
||||
|
||||
- [Monorepo](#monorepo)
|
||||
- [Community channels](#community-channels)
|
||||
|
||||
## Development setup
|
||||
|
||||
Thanks for your interest in Supabase and for wanting to contribute! Before you begin, read the
|
||||
[code of conduct](https://github.com/supabase/.github/blob/main/CODE_OF_CONDUCT.md) and check out the
|
||||
[existing issues](https://github.com/supabase/supabase/issues).
|
||||
This document describes how to set up your development environment to build and test Supabase.
|
||||
|
||||
### Install dependencies
|
||||
|
||||
You need to install and configure the following dependencies on your machine to build Supabase:
|
||||
|
||||
- [Git](http://git-scm.com/)
|
||||
- [Node.js v16.x (LTS)](http://nodejs.org)
|
||||
- [npm](https://www.npmjs.com/) version 7+ or [Yarn](https://yarnpkg.com/)
|
||||
|
||||
### Fork the repository
|
||||
|
||||
To contribute code to Supabase, you must fork the [Supabase Repository](https://github.com/supabase/supabase).
|
||||
|
||||
## Build Supabase
|
||||
|
||||
1. Clone your GitHub forked repository:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/<github_username>/supabase.git
|
||||
```
|
||||
|
||||
1. Go to the Supabase directory:
|
||||
```sh
|
||||
cd supabase
|
||||
```
|
||||
|
||||
### Run the docs locally
|
||||
|
||||
1. Build the development server
|
||||
|
||||
npm
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
or with yarn
|
||||
|
||||
```sh
|
||||
yarn build
|
||||
```
|
||||
|
||||
1. Start the development server
|
||||
|
||||
npm
|
||||
|
||||
```sh
|
||||
npm run start
|
||||
```
|
||||
|
||||
or with yarn
|
||||
|
||||
```sh
|
||||
yarn start
|
||||
```
|
||||
|
||||
1. Access the local server in your web browser at http://localhost:3010/docs.
|
||||
|
||||
---
|
||||
|
||||
### Editing files
|
||||
|
||||
Our docs site are comprised of guides, tutorials, quickstarts, and reference docs. Note that the [reference docs](https://supabase.com/docs/reference) are generated from spec files.
|
||||
|
||||
If you go to any page in the [Supabase docs](https://supabase.com/docs), you'll see an **Edit this page** link at the bottom. This link indicates which file you need to edit.
|
||||
|
||||
For example, the [NextJS Quickstart](https://supabase.com/docs/guides/with-nextjs) is a markdown file you can edit directly. However, this [signUp() reference doc](https://supabase.com/docs/reference/javascript/auth-signup) is generated from a [spec file](https://github.com/supabase/supabase/edit/master/spec/supabase_js_v1_legacy.yml).
|
||||
|
||||
## Create a pull request
|
||||
|
||||
After making your changes, open a pull request (PR). Once you submit your pull request, others from the Supabase team/community will review it with you.
|
||||
|
||||
Did you have an issue, like a merge conflict, or don't know how to open a pull request? Check out [GitHub's pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests) tutorial on how to resolve merge conflicts and other issues. Once your PR has been merged, you will be proudly listed as a contributor in the [contributor chart](https://github.com/supabase/supabase/graphs/contributors).
|
||||
|
||||
---
|
||||
|
||||
## Monorepo
|
||||
|
||||
We are in the process of migrating this repository to monorepo, using Turborepo.
|
||||
This app is currently not in that workflow, but if you want to contribute to any of the other apps, please refer to [the developers readme](https://github.com/supabase/supabase/blob/master/DEVELOPERS.md).
|
||||
|
||||
---
|
||||
|
||||
## Community channels
|
||||
|
||||
Stuck somewhere? Have any questions? Join the [Discord Community Server](https://discord.supabase.com/) or the [Github Discussions](https://github.com/supabase/supabase/discussions). We are here to help!
|
||||
@@ -17,8 +17,9 @@ Custom domains allow you to present a branded experience to your users. You can
|
||||
|
||||
To get started:
|
||||
|
||||
1. [Install](/docs/guides/cli) the Supabase CLI 1.11.3+.
|
||||
1. [Install](/docs/guides/cli) the Supabase CLI 1.11.6+.
|
||||
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 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
|
||||
|
||||
@@ -10,14 +10,18 @@
|
||||
"text": "Auth",
|
||||
"url": "/auth"
|
||||
},
|
||||
{
|
||||
"text": "Storage",
|
||||
"url": "/storage"
|
||||
},
|
||||
{
|
||||
"text": "Functions",
|
||||
"url": "/edge-functions"
|
||||
},
|
||||
{
|
||||
"text": "Realtime",
|
||||
"url": "/realtime"
|
||||
},
|
||||
{
|
||||
"text": "Storage",
|
||||
"url": "/storage"
|
||||
},
|
||||
{
|
||||
"text": "Pricing",
|
||||
"url": "/pricing"
|
||||
@@ -36,8 +40,8 @@
|
||||
"url": "/support"
|
||||
},
|
||||
{
|
||||
"text": "Brand Assets / Logos",
|
||||
"url": "/brand-assets"
|
||||
"text": "System Status",
|
||||
"url": "https://status.supabase.com/"
|
||||
},
|
||||
{
|
||||
"text": "Integrations",
|
||||
@@ -48,16 +52,8 @@
|
||||
"url": "/partners/experts"
|
||||
},
|
||||
{
|
||||
"text": "System Status",
|
||||
"url": "https://status.supabase.com/"
|
||||
},
|
||||
{
|
||||
"text": "Terms of service",
|
||||
"url": "/docs/company/terms"
|
||||
},
|
||||
{
|
||||
"text": "Privacy Policy",
|
||||
"url": "/docs/company/privacy"
|
||||
"text": "Brand Assets / Logos",
|
||||
"url": "/brand-assets"
|
||||
},
|
||||
{
|
||||
"text": "DPA",
|
||||
@@ -78,15 +74,31 @@
|
||||
},
|
||||
{
|
||||
"text": "API Reference",
|
||||
"url": "/docs/guides/api"
|
||||
},
|
||||
{
|
||||
"text": "Guides",
|
||||
"url": "/docs/"
|
||||
"url": "/docs/reference"
|
||||
},
|
||||
{
|
||||
"text": "Changelog",
|
||||
"url": "/changelog"
|
||||
},
|
||||
{
|
||||
"text": "Contributing",
|
||||
"url": "/docs/handbook/contributing"
|
||||
},
|
||||
{
|
||||
"text": "Open Source",
|
||||
"url": "/docs/oss"
|
||||
},
|
||||
{
|
||||
"text": "SupaSquad",
|
||||
"url": "/docs/handbook/supasquad"
|
||||
},
|
||||
{
|
||||
"text": "DevTo",
|
||||
"url": "https://dev.to/supabase"
|
||||
},
|
||||
{
|
||||
"text": "RSS",
|
||||
"url": "/rss.xml"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -97,10 +109,6 @@
|
||||
"text": "Blog",
|
||||
"url": "/blog"
|
||||
},
|
||||
{
|
||||
"text": "Open Source",
|
||||
"url": "/docs/oss"
|
||||
},
|
||||
{
|
||||
"text": "Careers",
|
||||
"url": "https://about.supabase.com/careers"
|
||||
@@ -109,6 +117,22 @@
|
||||
"text": "Company",
|
||||
"url": "/company"
|
||||
},
|
||||
{
|
||||
"text": "Terms of Service",
|
||||
"url": "/docs/company/terms"
|
||||
},
|
||||
{
|
||||
"text": "Privacy Policy",
|
||||
"url": "/docs/company/privacy"
|
||||
},
|
||||
{
|
||||
"text": "Acceptable Use Policy",
|
||||
"url": "/docs/company/aup"
|
||||
},
|
||||
{
|
||||
"text": "Service Level Agreement",
|
||||
"url": "/docs/company/sla"
|
||||
},
|
||||
{
|
||||
"text": "Humans.txt",
|
||||
"url": "/humans.txt"
|
||||
|
||||
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 254 KiB |
|
After Width: | Height: | Size: 477 KiB |
|
After Width: | Height: | Size: 967 KiB |
|
After Width: | Height: | Size: 667 KiB |
|
After Width: | Height: | Size: 674 KiB |
|
After Width: | Height: | Size: 479 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 678 KiB |
|
After Width: | Height: | Size: 928 KiB |
|
After Width: | Height: | Size: 484 KiB |
|
After Width: | Height: | Size: 794 KiB |
@@ -55,8 +55,8 @@ function Tabs({
|
||||
let __styles = styleHandler('tabs')
|
||||
|
||||
// activeId state can be overriden externally with `active`
|
||||
// defaults to use a url #hash if we have one or activeTab if not
|
||||
const active = activeId ? activeId : hash ? hash : activeTab
|
||||
// defaults to the first panelif we have one or url hash if not
|
||||
const active = activeId ? activeId : activeTab ? activeTab : hash
|
||||
|
||||
function onTabClick(id: string) {
|
||||
const newTabSelected = id !== active
|
||||
|
||||
@@ -337,6 +337,7 @@ pages:
|
||||
- The refresh token can only be used once to obtain a new session.
|
||||
- [Refresh token rotation](/docs/reference/auth/config#refresh_token_rotation_enabled) is enabled by default on all projects to guard against replay attacks.
|
||||
- You can configure the [`REFRESH_TOKEN_REUSE_INTERVAL`](https://supabase.com/docs/reference/auth/config#refresh_token_reuse_interval) which provides a short window in which the same refresh token can be used multiple times in the event of concurrency or offline issues.
|
||||
- If you are using React Native, you will need to install a Buffer polyfill via a library such as [rn-nodeify](https://github.com/tradle/rn-nodeify) to properly use the library.
|
||||
examples:
|
||||
- name: Refresh the session
|
||||
description: Sets the session data from refresh_token and returns current session or an error if the refresh_token is invalid.
|
||||
@@ -2711,7 +2712,7 @@ pages:
|
||||
examples:
|
||||
- name: Checking nullness
|
||||
description: |
|
||||
Using the `eq()` filter doesn't work when filtering for `null`:
|
||||
Using the `eq()` filter doesn't work when filtering for `null`, `true`, or `false`:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="schema" label="Schema">
|
||||
|
||||
@@ -139,7 +139,7 @@ const ExitSurvey: FC<Props> = ({ freeTier, subscription, onSelectBack }) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Submit feedback to Freshdesk
|
||||
// Submit exit survey to Hubspot
|
||||
const feedbackRes = await post(`${API_URL}/feedback/send`, {
|
||||
projectRef,
|
||||
subject: 'Subscription cancellation - Exit survey',
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { FC, useState } from 'react'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Button } from 'ui'
|
||||
import { Button, Input } from 'ui'
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { useStore, checkPermissions, useFlag } from 'hooks'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { delete_ } from 'lib/common/fetch'
|
||||
import { API_URL, PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
|
||||
import { delete_, post } from 'lib/common/fetch'
|
||||
import TextConfirmModal from 'components/ui/Modals/TextConfirmModal'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { CANCELLATION_REASONS } from 'components/interfaces/Billing/Billing.constants'
|
||||
import { generateFeedbackMessage } from 'components/interfaces/Billing/ExitSurvey/ExitSurvey.utils'
|
||||
|
||||
interface Props {
|
||||
type?: 'danger' | 'default'
|
||||
@@ -19,10 +21,23 @@ const DeleteProjectButton: FC<Props> = ({ type = 'danger' }) => {
|
||||
const enablePermissions = useFlag('enablePermissions')
|
||||
|
||||
const project = ui.selectedProject
|
||||
const projectRef = project?.ref
|
||||
const projectTier = project?.subscription_tier ?? PRICING_TIER_PRODUCT_IDS.FREE
|
||||
|
||||
const isOwner = ui.selectedOrganization?.is_owner
|
||||
const isFree = projectTier === PRICING_TIER_PRODUCT_IDS.FREE
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [selectedReasons, setSelectedReasons] = useState<string[]>([])
|
||||
const [cancellationMessage, setCancellationMessage] = useState<string>('')
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setSelectedReasons([])
|
||||
setCancellationMessage('')
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const canDeleteProject = enablePermissions
|
||||
? checkPermissions(PermissionAction.UPDATE, 'projects')
|
||||
@@ -33,12 +48,29 @@ const DeleteProjectButton: FC<Props> = ({ type = 'danger' }) => {
|
||||
setIsOpen(!isOpen)
|
||||
}
|
||||
|
||||
const onSelectCancellationReason = (reason: string) => {
|
||||
const existingSelection = selectedReasons.find((x) => x === reason)
|
||||
const updatedSelection =
|
||||
existingSelection === undefined
|
||||
? selectedReasons.concat([reason])
|
||||
: selectedReasons.filter((x) => x !== reason)
|
||||
setSelectedReasons(updatedSelection)
|
||||
}
|
||||
|
||||
async function handleDeleteProject() {
|
||||
if (project === undefined) return
|
||||
|
||||
if (!isFree && selectedReasons.length === 0) {
|
||||
return ui.setNotification({
|
||||
category: 'error',
|
||||
duration: 4000,
|
||||
message: 'Please select at least one reason for deleting your project',
|
||||
})
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
try {
|
||||
const response = await delete_(`${API_URL}/projects/${project.ref}`)
|
||||
const response = await delete_(`${API_URL}/projects/${projectRef}`)
|
||||
if (response.error) throw response.error
|
||||
app.onProjectDeleted(response)
|
||||
ui.setNotification({ category: 'success', message: `Successfully deleted ${project.name}` })
|
||||
@@ -50,6 +82,16 @@ const DeleteProjectButton: FC<Props> = ({ type = 'danger' }) => {
|
||||
message: `Failed to delete project ${project.name}: ${error.message}`,
|
||||
})
|
||||
}
|
||||
|
||||
// Submit exit survey to Hubspot
|
||||
const feedbackRes = await post(`${API_URL}/feedback/send`, {
|
||||
projectRef,
|
||||
subject: 'Subscription cancellation - Exit survey',
|
||||
tags: ['dashboard-exitsurvey'],
|
||||
category: 'Billing',
|
||||
message: generateFeedbackMessage(selectedReasons, cancellationMessage),
|
||||
})
|
||||
if (feedbackRes.error) throw feedbackRes.error
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -79,15 +121,80 @@ const DeleteProjectButton: FC<Props> = ({ type = 'danger' }) => {
|
||||
<TextConfirmModal
|
||||
visible={isOpen}
|
||||
loading={loading}
|
||||
size={isFree ? 'small' : 'xlarge'}
|
||||
title={`Confirm deletion of ${project?.name}`}
|
||||
alert={
|
||||
isFree
|
||||
? 'This action cannot be undone.'
|
||||
: `This will permanently delete the ${project?.name} project and all of its data, and cannot be undone`
|
||||
}
|
||||
text={
|
||||
isFree
|
||||
? `This will permanently delete the ${project?.name} project and all of its data.`
|
||||
: undefined
|
||||
}
|
||||
confirmPlaceholder="Type the project name in here"
|
||||
alert="This action cannot be undone."
|
||||
text={`This will permanently delete the ${project?.name} project and all of its data.`}
|
||||
confirmString={project?.name || ''}
|
||||
confirmLabel="I understand, delete this project"
|
||||
onConfirm={handleDeleteProject}
|
||||
onCancel={toggle}
|
||||
/>
|
||||
>
|
||||
{/*
|
||||
[Joshen] This is basically ExitSurvey.tsx, ideally we have one shared component but the one
|
||||
in ExitSurvey has a Form wrapped around it already. Will probably need some effort to refactor
|
||||
but leaving that for the future.
|
||||
*/}
|
||||
{!isFree && (
|
||||
<>
|
||||
<div className="space-y-1">
|
||||
<h4 className="text-base">We're sad that you're leaving.</h4>
|
||||
<p className="text-sm text-scale-1100">
|
||||
We always strive to improve Supabase as much as we can. Please let us know the
|
||||
reasons you are deleting your project so that we can improve in the future.
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-4 pt-4">
|
||||
<div className="flex flex-wrap gap-2" data-toggle="buttons">
|
||||
{CANCELLATION_REASONS.map((option) => {
|
||||
const active = selectedReasons.find((x) => x === option)
|
||||
return (
|
||||
<label
|
||||
key={option}
|
||||
className={[
|
||||
'flex cursor-pointer items-center space-x-2 rounded-md py-1',
|
||||
'pl-2 pr-3 text-center text-sm shadow-sm transition-all duration-100',
|
||||
`${
|
||||
active
|
||||
? ` bg-scale-1200 text-scale-100 opacity-100 hover:bg-opacity-75`
|
||||
: ` bg-scale-700 text-scale-1200 opacity-25 hover:opacity-50`
|
||||
}`,
|
||||
].join(' ')}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="options"
|
||||
value={option}
|
||||
className="hidden"
|
||||
onClick={(event: any) => onSelectCancellationReason(event.target.value)}
|
||||
/>
|
||||
<div>{option}</div>
|
||||
</label>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className="text-area-text-sm">
|
||||
<Input.TextArea
|
||||
name="message"
|
||||
label="Anything else that we can improve on?"
|
||||
rows={3}
|
||||
value={cancellationMessage}
|
||||
onChange={(event) => setCancellationMessage(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</TextConfirmModal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ interface Props {
|
||||
}
|
||||
|
||||
/*
|
||||
The information heirarchy for WithSidebar is:
|
||||
The information hierarchy for WithSidebar is:
|
||||
WithSidebar
|
||||
SectionsWithHeaders
|
||||
SidebarItem
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import dayjs from 'dayjs'
|
||||
import useSWR from 'swr'
|
||||
import { FC } from 'react'
|
||||
import { FC, useState } from 'react'
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { Notification, NotificationStatus } from '@supabase/shared-types/out/notifications'
|
||||
|
||||
import { useStore } from 'hooks'
|
||||
import { useStore, useNotifications } from 'hooks'
|
||||
import { Project } from 'types'
|
||||
import { formatNotificationCTAText, formatNotificationText } from './NotificationRows.utils'
|
||||
import NotificationActions from './NotificationActions'
|
||||
import { get } from 'lib/common/fetch'
|
||||
import { get, delete_ } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { Button, IconX } from 'ui'
|
||||
|
||||
interface Props {
|
||||
notification: Notification
|
||||
@@ -25,7 +27,9 @@ const NotificationRow: FC<Props> = ({
|
||||
onSelectRollbackMigration,
|
||||
onSelectFinalizeMigration,
|
||||
}) => {
|
||||
const { app } = useStore()
|
||||
const { app, ui } = useStore()
|
||||
const { refresh } = useNotifications()
|
||||
const [dismissing, setDismissing] = useState(false)
|
||||
const [project] = app.projects.list((project: Project) => project.id === notification.project_id)
|
||||
|
||||
const insertedAt = dayjs(notification.inserted_at).format('DD MMM YYYY, HH:mma')
|
||||
@@ -42,6 +46,23 @@ const NotificationRow: FC<Props> = ({
|
||||
get
|
||||
)
|
||||
|
||||
const dismissNotification = async (notificationId: string) => {
|
||||
if (!notificationId) return
|
||||
setDismissing(true)
|
||||
const { error } = await delete_(`${API_URL}/notifications`, { ids: [notificationId] })
|
||||
if (error) {
|
||||
ui.setNotification({
|
||||
category: 'error',
|
||||
message: 'Failed to dismiss notification',
|
||||
error,
|
||||
duration: 4000,
|
||||
})
|
||||
} else {
|
||||
refresh()
|
||||
}
|
||||
setDismissing(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex py-2">
|
||||
<div className="flex min-w-[50px] justify-center">
|
||||
@@ -49,14 +70,48 @@ const NotificationRow: FC<Props> = ({
|
||||
<div className="mt-1.5 h-2 w-2 rounded-full bg-green-900" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-grow mr-8 flex items-center space-x-4">
|
||||
<div className="w-[70%] space-y-2">
|
||||
{formatNotificationText(project, notification, ownerReassignStatus)}
|
||||
{formatNotificationCTAText(availableActions, ownerReassignStatus)}
|
||||
<p className="text-scale-1100 text-sm !mt-2">{insertedAt}</p>
|
||||
<div className="flex-grow mr-8 flex flex-col gap-4">
|
||||
<div className="w-full flex justify-end">
|
||||
<div className="w-9/10 space-y-2">
|
||||
{formatNotificationText(project, notification, ownerReassignStatus)}
|
||||
{formatNotificationCTAText(availableActions, ownerReassignStatus)}
|
||||
<p className="text-scale-1100 text-sm !mt-2">{insertedAt}</p>
|
||||
</div>
|
||||
<div className="w-1/10 flex justify-end">
|
||||
<div>
|
||||
<Tooltip.Root delayDuration={0}>
|
||||
<Tooltip.Trigger>
|
||||
<Button
|
||||
className="!px-1 group"
|
||||
type="text"
|
||||
loading={dismissing}
|
||||
icon={
|
||||
<IconX
|
||||
size={14}
|
||||
strokeWidth={2}
|
||||
className="text-scale-1100 group-hover:text-scale-1200 transition"
|
||||
/>
|
||||
}
|
||||
onClick={() => dismissNotification(notification.id)}
|
||||
/>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side="bottom">
|
||||
<Tooltip.Arrow className="radix-tooltip-arrow" />
|
||||
<div
|
||||
className={[
|
||||
'rounded bg-scale-100 py-1 px-2 leading-none shadow',
|
||||
'border border-scale-200',
|
||||
].join(' ')}
|
||||
>
|
||||
<span className="text-xs text-scale-1200">Dismiss</span>
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[30%] col-span-3 flex items-center justify-end">
|
||||
{availableActions.length > 0 && (
|
||||
{(availableActions.length > 0 || changelogLink !== undefined) && (
|
||||
<div className="flex items-center">
|
||||
<NotificationActions
|
||||
project={project}
|
||||
changelogLink={changelogLink}
|
||||
@@ -67,8 +122,8 @@ const NotificationRow: FC<Props> = ({
|
||||
onSelectRollbackMigration={() => onSelectRollbackMigration(project, notification)}
|
||||
onSelectFinalizeMigration={() => onSelectFinalizeMigration(project, notification)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -146,7 +146,7 @@ export const formatNotificationText = (
|
||||
</div>
|
||||
)
|
||||
} else if (notification.data.name === NotificationName.ProjectInformational) {
|
||||
return notification.data.message
|
||||
return <p className="text-sm">{notification.data.message}</p>
|
||||
} else {
|
||||
return (
|
||||
<p className="text-sm">
|
||||
@@ -177,7 +177,9 @@ export const formatNotificationCTAText = (
|
||||
return (
|
||||
<p className="text-sm space-x-1">
|
||||
This patch was applied on{' '}
|
||||
{dayjs(new Date(ownerReassignStatus.migrated_at ?? action.deadline)).format('DD MMM YYYY, HH:mma')}
|
||||
{dayjs(new Date(ownerReassignStatus.migrated_at ?? action.deadline)).format(
|
||||
'DD MMM YYYY, HH:mma'
|
||||
)}
|
||||
</p>
|
||||
)
|
||||
} else if (ownerReassignStatus?.desired === 'temp_role') {
|
||||
|
||||
@@ -99,7 +99,9 @@ const NotificationsPopover: FC<Props> = () => {
|
||||
if (!projectToApplyMigration) return
|
||||
const res = await post(`${API_URL}/database/${projectToApplyMigration.ref}/owner-reassign`, {})
|
||||
if (!res.error) {
|
||||
await app.projects.fetchDetail(projectToApplyMigration.ref, (project) => meta.setProjectDetails(project))
|
||||
await app.projects.fetchDetail(projectToApplyMigration.ref, (project) =>
|
||||
meta.setProjectDetails(project)
|
||||
)
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: `Successfully applied migration for project "${projectToApplyMigration.name}"`,
|
||||
@@ -121,7 +123,9 @@ const NotificationsPopover: FC<Props> = () => {
|
||||
{}
|
||||
)
|
||||
if (!res.error) {
|
||||
await app.projects.fetchDetail(projectToRollbackMigration.ref, (project) => meta.setProjectDetails(project))
|
||||
await app.projects.fetchDetail(projectToRollbackMigration.ref, (project) =>
|
||||
meta.setProjectDetails(project)
|
||||
)
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: `Successfully rolled back migration for project "${projectToRollbackMigration.name}"`,
|
||||
@@ -143,7 +147,9 @@ const NotificationsPopover: FC<Props> = () => {
|
||||
{}
|
||||
)
|
||||
if (!res.error) {
|
||||
await app.projects.fetchDetail(projectToFinalizeMigration.ref, (project) => meta.setProjectDetails(project))
|
||||
await app.projects.fetchDetail(projectToFinalizeMigration.ref, (project) =>
|
||||
meta.setProjectDetails(project)
|
||||
)
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: `Successfully finalized migration for project "${projectToFinalizeMigration.name}"`,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { toJS } from 'mobx'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Button, Dropdown, Divider, IconPlus } from 'ui'
|
||||
import { Button, Dropdown, IconPlus } from 'ui'
|
||||
|
||||
import { useStore } from 'hooks'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
@@ -20,11 +21,35 @@ const OrgDropdown = () => {
|
||||
<>
|
||||
{sortedOrganizations
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((x) => (
|
||||
<Dropdown.Item key={x.slug} onClick={() => router.push(`/org/${x.slug}/settings`)}>
|
||||
{x.name}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
.map((x) => {
|
||||
const slug = toJS(x.slug)
|
||||
|
||||
return (
|
||||
<Dropdown.Item
|
||||
key={slug}
|
||||
onClick={() => {
|
||||
if (!slug) {
|
||||
// The user should not see this error as the page should
|
||||
// be rerendered with the value of slug before they can click.
|
||||
// It is just here in case they are the flash.
|
||||
return ui.setNotification({
|
||||
category: 'error',
|
||||
message:
|
||||
'Could not navigate to organization settings, please try again or contact support',
|
||||
})
|
||||
}
|
||||
|
||||
router.push({
|
||||
pathname: `/org/[slug]/settings`,
|
||||
query: { slug },
|
||||
hash: router.asPath.split('#')[1]?.toLowerCase(),
|
||||
})
|
||||
}}
|
||||
>
|
||||
{x.name}
|
||||
</Dropdown.Item>
|
||||
)
|
||||
})}
|
||||
<Dropdown.Separator />
|
||||
<Dropdown.Item icon={<IconPlus size="tiny" />} onClick={() => router.push(`/new`)}>
|
||||
New organization
|
||||
|
||||
@@ -5,17 +5,20 @@ interface Props {
|
||||
loading: boolean
|
||||
visible: boolean
|
||||
title: string
|
||||
size?: 'tiny' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge'
|
||||
confirmLabel: string
|
||||
confirmPlaceholder: string
|
||||
confirmString: string
|
||||
alert: string
|
||||
text: string | ReactNode
|
||||
alert?: string
|
||||
text?: string | ReactNode
|
||||
children?: ReactNode
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
const TextConfirmModal: FC<Props> = ({
|
||||
title,
|
||||
size = 'small',
|
||||
onConfirm,
|
||||
visible,
|
||||
onCancel,
|
||||
@@ -25,6 +28,7 @@ const TextConfirmModal: FC<Props> = ({
|
||||
confirmString,
|
||||
alert,
|
||||
text,
|
||||
children,
|
||||
}) => {
|
||||
// [Joshen] Have to keep the loading prop here as this component itself doesn't
|
||||
// directly trigger any potential async job that follows onConfirm. It only triggers
|
||||
@@ -42,7 +46,7 @@ const TextConfirmModal: FC<Props> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal hideFooter closable size="small" visible={visible} header={title} onCancel={onCancel}>
|
||||
<Modal hideFooter closable size={size} visible={visible} header={title} onCancel={onCancel}>
|
||||
<Form
|
||||
validateOnBlur
|
||||
initialValues={{ confirmValue: '' }}
|
||||
@@ -52,14 +56,22 @@ const TextConfirmModal: FC<Props> = ({
|
||||
{() => (
|
||||
<div className="w-full py-4">
|
||||
<div className="space-y-4">
|
||||
{children && (
|
||||
<>
|
||||
<Modal.Content>{children}</Modal.Content>
|
||||
<Modal.Separator />
|
||||
</>
|
||||
)}
|
||||
{alert && (
|
||||
<Modal.Content>
|
||||
<Alert variant="warning" withIcon title={alert} />
|
||||
</Modal.Content>
|
||||
)}
|
||||
<Modal.Content>
|
||||
<p className="mb-2 block text-sm">{text}</p>
|
||||
</Modal.Content>
|
||||
{text !== undefined && (
|
||||
<Modal.Content>
|
||||
<p className="mb-2 block text-sm">{text}</p>
|
||||
</Modal.Content>
|
||||
)}
|
||||
<Modal.Separator />
|
||||
<Modal.Content>
|
||||
<Input
|
||||
|
||||
@@ -35,6 +35,51 @@ const nextConfig = {
|
||||
destination: '/project/:ref/settings/general',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/project/:ref/auth/settings',
|
||||
destination: '/project/:ref/auth/users',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/project/:ref/settings/billing',
|
||||
destination: '/project/:ref/settings/billing/subscription',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/project/:ref/database/postgres-logs',
|
||||
destination: '/project/:ref/logs/postgres-logs',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/project/:ref/database/postgrest-logs',
|
||||
destination: '/project/:ref/logs/postgrest-logs',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/project/:ref/database/pgbouncer-logs',
|
||||
destination: '/project/:ref/logs/pgbouncer-logs',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/project/:ref/database/realtime-logs',
|
||||
destination: '/project/:ref/logs/realtime-logs',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/project/:ref/storage/logs',
|
||||
destination: '/project/:ref/logs/storage-logs',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/project/:ref/auth/logs',
|
||||
destination: '/project/:ref/logs/auth-logs',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/project/:ref/logs-explorer',
|
||||
destination: '/project/:ref/logs/explorer',
|
||||
permanent: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
async headers() {
|
||||
|
||||
@@ -130,13 +130,14 @@ const OrganizationSettings: NextPageWithLayout = () => {
|
||||
// It is just here in case they are the flash.
|
||||
return ui.setNotification({
|
||||
category: 'error',
|
||||
message: 'Please try again',
|
||||
message: 'Could not navigate to organization settings, please try again or contact support',
|
||||
})
|
||||
}
|
||||
|
||||
setSelectedTab(id)
|
||||
router.push({
|
||||
pathname: `/org/${slug}/settings`,
|
||||
pathname: `/org/[slug]/settings`,
|
||||
query: { slug },
|
||||
hash: id.toLowerCase(),
|
||||
})
|
||||
}
|
||||
|
||||