Merge branch 'master' into thor/lw7-tickets-referrals
No files matched your search
@@ -25,4 +25,4 @@ jobs:
|
||||
with:
|
||||
version: 1.0.0
|
||||
|
||||
- run: supabase functions deploy og-images --project-ref $PROJECT_ID
|
||||
- run: supabase functions deploy og-images --project-ref $PROJECT_ID --no-verify-jwt
|
||||
@@ -51,3 +51,15 @@ jobs:
|
||||
# reporter: github-pr-review
|
||||
# level: warning
|
||||
# command: npx prettier -c 'i18n/**/*.{js,jsx,ts,tsx,css,md,json}'
|
||||
|
||||
format-sql:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Install plugin
|
||||
run: npm ci
|
||||
- name: Run prettier
|
||||
run: |-
|
||||
# Check mdx files which contain sql code blocks
|
||||
grep -lr '```sql' apps/docs/pages/**/*.mdx | xargs npx prettier -c
|
||||
@@ -4,4 +4,10 @@ node_modules
|
||||
package-lock.json
|
||||
docker*
|
||||
apps/**/out
|
||||
**/**.mdx
|
||||
# prettier-plugin-sql-cst only supports sqlite syntax
|
||||
**/supabase/migrations/*.sql
|
||||
apps/www/schema.sql
|
||||
examples/slack-clone/nextjs-slack-clone/full-schema.sql
|
||||
# ignore files with custom js formatting
|
||||
apps/docs/pages/guides/auth/*.mdx
|
||||
apps/docs/pages/guides/integrations/*.mdx
|
||||
@@ -4,5 +4,6 @@
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"endOfLine": "lf"
|
||||
"endOfLine": "lf",
|
||||
"sqlKeywordCase": "lower"
|
||||
}
|
||||
@@ -41,7 +41,7 @@ To see how to Contribute, visit [Getting Started](./DEVELOPERS.md)
|
||||
- [x] Alpha: We are testing Supabase with a closed set of customers
|
||||
- [x] Public Alpha: Anyone can sign up over at [app.supabase.com](https://app.supabase.com). But go easy on us, there are a few kinks
|
||||
- [x] Public Beta: Stable enough for most non-enterprise use-cases
|
||||
- [ ] Public: Production-ready
|
||||
- [ ] Public: General Availability [[status](https://supabase.com/docs/guides/getting-started/features#feature-status)]
|
||||
|
||||
We are currently in Public Beta. Watch "releases" of this repo to get notified of major updates.
|
||||
|
||||
|
||||
@@ -94,8 +94,15 @@ export default function Extensions() {
|
||||
filters.length === 0 ? x : x.tags.some((item) => filters.includes(item))
|
||||
)
|
||||
.map((extension) => (
|
||||
<Link passHref href={`/guides/database/extensions/${extension.name}`}>
|
||||
<a target="_blank" className="no-underline">
|
||||
<Link
|
||||
passHref
|
||||
href={`${
|
||||
extension.link
|
||||
? `/guides/database/extensions/${extension.name}`
|
||||
: '/guides/database/extensions#full-list-of-extensions'
|
||||
}`}
|
||||
>
|
||||
<a target={`${extension.link ? '_blank' : '_self'}`} className="no-underline">
|
||||
<GlassPanel title={extension.name} background={false} key={extension.name}>
|
||||
<p className="mt-4">
|
||||
{extension.comment.charAt(0).toUpperCase() + extension.comment.slice(1)}
|
||||
|
||||
@@ -512,6 +512,24 @@ export const database = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Postgres resources',
|
||||
url: undefined,
|
||||
items: [
|
||||
{
|
||||
name: 'Drop all tables in schema',
|
||||
url: '/guides/database/postgres/dropping-all-tables-in-schema',
|
||||
},
|
||||
{
|
||||
name: 'Select first row per group',
|
||||
url: '/guides/database/postgres/first-row-in-group',
|
||||
},
|
||||
{
|
||||
name: 'Print PostgreSQL version',
|
||||
url: '/guides/database/postgres/which-version-of-postgres',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Configuration',
|
||||
url: undefined,
|
||||
@@ -637,7 +655,7 @@ export const platform = {
|
||||
url: undefined,
|
||||
items: [
|
||||
{ name: 'Access Control', url: '/guides/platform/access-control', items: [] },
|
||||
{ name: 'Database Usage', url: '/guides/platform/database-usage', items: [] },
|
||||
{ name: 'Database Size', url: '/guides/platform/database-size', items: [] },
|
||||
{ name: 'HTTP Status Codes', url: '/guides/platform/http-status-codes', items: [] },
|
||||
{ name: 'Logging', url: '/guides/platform/logs', items: [] },
|
||||
{ name: 'Metrics', url: '/guides/platform/metrics', items: [] },
|
||||
@@ -727,24 +745,6 @@ export const resources = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Postgres resources',
|
||||
url: undefined,
|
||||
items: [
|
||||
{
|
||||
name: 'Drop all tables in schema',
|
||||
url: '/guides/resources/postgres/dropping-all-tables-in-schema',
|
||||
},
|
||||
{
|
||||
name: 'Select first row per group',
|
||||
url: '/guides/resources/postgres/first-row-in-group',
|
||||
},
|
||||
{
|
||||
name: 'Print PostgreSQL version',
|
||||
url: '/guides/resources/postgres/which-version-of-postgres',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +1,68 @@
|
||||
import { MDXProvider } from '@mdx-js/react'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FC } from 'react'
|
||||
import components from '~/components'
|
||||
import TableOfContents from '~/components/TableOfContents'
|
||||
|
||||
interface Props {
|
||||
meta: { title: string; description?: string; hide_table_of_contents?: boolean; video?: string }
|
||||
meta: {
|
||||
title: string
|
||||
description?: string
|
||||
hide_table_of_contents?: boolean
|
||||
video?: string
|
||||
canonical?: string
|
||||
}
|
||||
children: any
|
||||
toc?: any
|
||||
menuItems: any
|
||||
}
|
||||
|
||||
const Layout: FC<Props> = (props: Props) => {
|
||||
const { asPath, basePath } = useRouter()
|
||||
|
||||
const hasTableOfContents =
|
||||
props.toc !== undefined &&
|
||||
props.toc.json.filter((item) => item.lvl !== 1 && item.lvl <= 3).length > 0
|
||||
|
||||
console.log('asPath', asPath)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{props.meta?.title} | Supabase Docs</title>
|
||||
<title>
|
||||
{asPath === '/' ? 'Supabase Docs' : `${props.meta?.title} | Supabase Docadsdsds`}
|
||||
</title>
|
||||
<meta name="description" content={props.meta?.description} />
|
||||
<meta property="og:image" content={`https://supabase.com/docs/img/supabase-og-image.png`} />
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content={`https://supabase.com/docs/img/supabase-og-image.png`}
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="/docs/favicon.ico" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content={props.meta?.title} />
|
||||
<meta property="og:description" content={props.meta?.description} />
|
||||
<meta property="og:title" content={props.meta?.title} />
|
||||
</Head>
|
||||
|
||||
<NextSeo
|
||||
canonical={props.meta?.canonical ?? `https://supabase.com/docs${asPath}`}
|
||||
openGraph={{
|
||||
url: `https://supabase.com/docs${asPath}`,
|
||||
type: 'article',
|
||||
videos: props.meta?.video && [
|
||||
{
|
||||
// youtube based video meta
|
||||
url: props.meta?.video,
|
||||
width: 640,
|
||||
height: 385,
|
||||
type: 'application/x-shockwave-flash',
|
||||
},
|
||||
],
|
||||
article: {
|
||||
publishedTime: new Date().toISOString(),
|
||||
modifiedTime: new Date().toISOString(),
|
||||
authors: ['Supabase'],
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<div className={['relative transition-all ease-out', 'duration-150 '].join(' ')}>
|
||||
{/* <p className="text-brand-900 tracking-wider">Tutorials</p> */}
|
||||
<article className="prose dark:prose-dar max-w-none">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { MDXProvider } from '@mdx-js/react'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { FC, useEffect, useRef, useState } from 'react'
|
||||
@@ -63,17 +64,27 @@ const Layout: FC<Props> = (props) => {
|
||||
|
||||
const hasTableOfContents = tocList.length > 0
|
||||
|
||||
// page type, ie, Auth, Database, Storage etc
|
||||
const ogPageType = asPath.split('/')[2]
|
||||
// open graph image url constructor
|
||||
const ogImageUrl = `https://obuldanrptloktxcffvn.functions.supabase.co/og-images?site=docs${
|
||||
ogPageType ? `&type=${ogPageType}` : ''
|
||||
}&title=${encodeURIComponent(props.meta?.title)}&description=${encodeURIComponent(
|
||||
props.meta?.description
|
||||
)}`
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{props.meta?.title} | Supabase Docs</title>
|
||||
<meta name="description" content={props.meta?.description} />
|
||||
<meta property="og:image" content={ogImageUrl} />
|
||||
<meta name="twitter:image" content={ogImageUrl} />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
</Head>
|
||||
<NextSeo
|
||||
title={`${props.meta?.title} | Supabase Docs`}
|
||||
description={props.meta?.description ? props.meta?.description : props.meta?.title}
|
||||
canonical={props.meta?.canonical ?? `https://supabase.com/docs${asPath}`}
|
||||
openGraph={{
|
||||
title: props.meta?.title,
|
||||
description: props.meta?.description,
|
||||
url: `https://supabase.com/docs${asPath}`,
|
||||
type: 'article',
|
||||
videos: props.meta?.video && [
|
||||
@@ -90,15 +101,6 @@ const Layout: FC<Props> = (props) => {
|
||||
modifiedTime: new Date().toISOString(),
|
||||
authors: ['Supabase'],
|
||||
},
|
||||
images: [
|
||||
{
|
||||
url: `https://obuldanrptloktxcffvn.functions.supabase.co/og-images?site=docs${
|
||||
ogPageType ? `&type=${ogPageType}` : ''
|
||||
}&title=${encodeURIComponent(props.meta?.title)}&description=${encodeURIComponent(
|
||||
props.meta?.description
|
||||
)}`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<div className={['grid grid-cols-12 relative gap-4'].join(' ')}>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
"@radix-ui/react-accordion": "^1.0.1",
|
||||
"@supabase/auth-helpers-nextjs": "^0.5.4",
|
||||
"@supabase/auth-helpers-react": "^0.3.1",
|
||||
"@supabase/supabase-js": "^2.4.1",
|
||||
"@supabase/supabase-js": "^2.8.0",
|
||||
"algoliasearch": "^4.14.2",
|
||||
"babel": "^6.23.0",
|
||||
"clsx": "^1.2.1",
|
||||
@@ -101,7 +101,6 @@
|
||||
"next-transpile-modules": "9.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"openapi-types": "^12.0.2",
|
||||
"prettier": "^2.2.1",
|
||||
"sass": "^1.55.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig": "*",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { SessionContextProvider } from '@supabase/auth-helpers-react'
|
||||
import { ThemeProvider } from 'common/Providers'
|
||||
import { DefaultSeo } from 'next-seo'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { AppPropsWithLayout } from 'types'
|
||||
@@ -9,8 +10,8 @@ import ClippyProvider from '~/components/Clippy/ClippyProvider'
|
||||
import { SearchProvider } from '~/components/DocSearch'
|
||||
import Favicons from '~/components/Favicons'
|
||||
import SiteLayout from '~/layouts/SiteLayout'
|
||||
import { post } from '~/lib/fetchWrappers'
|
||||
import { IS_PLATFORM } from '~/lib/constants'
|
||||
import { post } from '~/lib/fetchWrappers'
|
||||
import '../styles/algolia-search.scss'
|
||||
import '../styles/ch.scss'
|
||||
import '../styles/docsearch.scss'
|
||||
@@ -59,34 +60,10 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
||||
}, [router.events])
|
||||
|
||||
const SITE_TITLE = 'Supabase Documentation'
|
||||
const SITE_DESCRIPTION = 'The open source Firebase alternative.'
|
||||
const { basePath } = useRouter()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Favicons />
|
||||
<DefaultSeo
|
||||
title={SITE_TITLE}
|
||||
description={SITE_DESCRIPTION}
|
||||
openGraph={{
|
||||
type: 'website',
|
||||
url: 'https://supabase.com/docs',
|
||||
site_name: SITE_TITLE,
|
||||
images: [
|
||||
{
|
||||
url: `https://supabase.com${basePath}/img/supabase-og-image.png`,
|
||||
width: 800,
|
||||
height: 600,
|
||||
alt: 'Supabase Og Image',
|
||||
},
|
||||
],
|
||||
}}
|
||||
twitter={{
|
||||
handle: '@supabase',
|
||||
site: '@supabase',
|
||||
cardType: 'summary_large_image',
|
||||
}}
|
||||
/>
|
||||
{IS_PLATFORM ? (
|
||||
<SessionContextProvider supabaseClient={supabase}>
|
||||
<ThemeProvider>
|
||||
|
||||
@@ -38,6 +38,12 @@ Primary keys are **guaranteed not to change**. Columns, indices, constraints or
|
||||
|
||||
You may delete users directly or via the management console at Authentication > Users. Note that deleting a user from the `auth.users` table does not automatically sign out a user. As Supabase makes use of JSON Web Tokens (JWT), a user's JWT will remain "valid" until it has expired. Should you wish to immediately revoke access for a user, do considering making use of a Row Level Security policy as described below.
|
||||
|
||||
<Admonition type="caution">
|
||||
You cannot delete a user if they are the owner of any objects in Supabase Storage.
|
||||
|
||||
You will encounter an error when you try to delete an Auth user that owns any Storage objects. If this happens, try deleting all the objects for that user, or reassign ownership to another user.
|
||||
</Admonition>
|
||||
|
||||
## Public access
|
||||
|
||||
Since Row Level Security is enabled, this table is accessible via the API but no data will be returned unless we set up some Policies.
|
||||
|
||||
@@ -329,6 +329,129 @@ Tip: Make sure to enable RLS for all your tables, so that your tables are inacce
|
||||
Supabase provides special "Service" keys, which can be used to bypass all RLS.
|
||||
These should never be used in the browser or exposed to customers, but they are useful for administrative tasks.
|
||||
|
||||
### Testing policies
|
||||
|
||||
To test policies on the database itself (i.e., from the [SQL Editor](https://app.supabase.com/project/_/sql) or from `psql`) without switching to your frontend and logging in as different users, you can utilize the following helper SQL procedures ([credits](https://github.com/supabase/supabase/issues/7311#issuecomment-1398648114)):
|
||||
|
||||
```sql
|
||||
grant anon, authenticated to postgres;
|
||||
|
||||
create or replace procedure auth.login_as_user (user_email text)
|
||||
language plpgsql
|
||||
as $$
|
||||
declare
|
||||
auth_user auth.users;
|
||||
begin
|
||||
select
|
||||
* into auth_user
|
||||
from
|
||||
auth.users
|
||||
where
|
||||
email = user_email;
|
||||
execute format('set request.jwt.claim.sub=%L', (auth_user).id::text);
|
||||
execute format('set request.jwt.claim.role=%I', (auth_user).role);
|
||||
execute format('set request.jwt.claim.email=%L', (auth_user).email);
|
||||
execute format('set request.jwt.claims=%L', json_strip_nulls(json_build_object('app_metadata', (auth_user).raw_app_meta_data))::text);
|
||||
|
||||
raise notice '%', format( 'set role %I; -- logging in as %L (%L)', (auth_user).role, (auth_user).id, (auth_user).email);
|
||||
execute format('set role %I', (auth_user).role);
|
||||
end;
|
||||
$$;
|
||||
|
||||
create or replace procedure auth.login_as_anon ()
|
||||
language plpgsql
|
||||
as $$
|
||||
begin
|
||||
set request.jwt.claim.sub='';
|
||||
set request.jwt.claim.role='';
|
||||
set request.jwt.claim.email='';
|
||||
set request.jwt.claims='';
|
||||
set role anon;
|
||||
end;
|
||||
$$;
|
||||
|
||||
create or replace procedure auth.logout ()
|
||||
language plpgsql
|
||||
as $$
|
||||
begin
|
||||
set request.jwt.claim.sub='';
|
||||
set request.jwt.claim.role='';
|
||||
set request.jwt.claim.email='';
|
||||
set request.jwt.claims='';
|
||||
set role postgres;
|
||||
end;
|
||||
$$;
|
||||
```
|
||||
|
||||
To switch to a given user (by email), use `call auth.login_as_user('my@email.com');`. You can also switch to the `anon` role using `call auth.login_as_anon();`. When you are done, use `call auth.logout();` to return yourself to the `postgres` role.
|
||||
|
||||
These procedures can also be used for writing [pgTAP](/docs/guides/database/extensions/pgtap) unit tests for policies.
|
||||
|
||||
<details>
|
||||
<summary>Click here to see an example `psql` interaction using this.</summary>
|
||||
|
||||
This example shows that the `public.profiles` table from the tutorial example can indeed be updated by the `postgres` role and the owner of the row but not from `anon` connections:
|
||||
|
||||
```shell
|
||||
postgres=> select id, email from auth.users;
|
||||
id | email
|
||||
--------------------------------------+-------------------
|
||||
d4f0aa86-e6f6-41d1-bd32-391f077cf1b9 | user1@example.com
|
||||
15d6811a-16ee-4fa2-9b18-b63085688be4 | user2@example.com
|
||||
4e1010bb-eb37-4a4d-a05a-b0ee315c9d56 | user3@example.com
|
||||
(3 rows)
|
||||
|
||||
postgres=> table public.profiles;
|
||||
id | updated_at | username | full_name | avatar_url | website
|
||||
--------------------------------------+------------+----------+-----------+------------+---------
|
||||
d4f0aa86-e6f6-41d1-bd32-391f077cf1b9 | | user1 | User 1 | |
|
||||
15d6811a-16ee-4fa2-9b18-b63085688be4 | | user2 | User 2 | |
|
||||
4e1010bb-eb37-4a4d-a05a-b0ee315c9d56 | | user3 | User 3 | |
|
||||
(3 rows)
|
||||
|
||||
postgres=> call auth.login_as_anon();
|
||||
CALL
|
||||
postgres=> update public.profiles set updated_at=now();
|
||||
UPDATE 0 -- anon users cannot update any profile but see all of them
|
||||
postgres=> table public.profiles;
|
||||
id | updated_at | username | full_name | avatar_url | website
|
||||
--------------------------------------+------------+----------+-----------+------------+---------
|
||||
d4f0aa86-e6f6-41d1-bd32-391f077cf1b9 | | user1 | User 1 | |
|
||||
15d6811a-16ee-4fa2-9b18-b63085688be4 | | user2 | User 2 | |
|
||||
4e1010bb-eb37-4a4d-a05a-b0ee315c9d56 | | user3 | User 3 | |
|
||||
(3 rows)
|
||||
|
||||
postgres=> call auth.logout();
|
||||
CALL
|
||||
postgres=> call auth.login_as_user('user1@example.com');
|
||||
NOTICE: set role authenticated; -- logging in as 'd4f0aa86-e6f6-41d1-bd32-391f077cf1b9' ('user1@example.com')
|
||||
CALL
|
||||
postgres=> update public.profiles set updated_at=now();
|
||||
UPDATE 1 -- authenticated users can update their own profile and see all of them
|
||||
postgres=> table public.profiles;
|
||||
id | updated_at | username | full_name | avatar_url | website
|
||||
--------------------------------------+-------------------------------+----------+-----------+------------+---------
|
||||
15d6811a-16ee-4fa2-9b18-b63085688be4 | | user1 | User 1 | |
|
||||
4e1010bb-eb37-4a4d-a05a-b0ee315c9d56 | | user2 | User 2 | |
|
||||
d4f0aa86-e6f6-41d1-bd32-391f077cf1b9 | 2023-02-18 21:39:16.204612+00 | user3 | User 3 | |
|
||||
(3 rows)
|
||||
|
||||
postgres=> call auth.logout();
|
||||
CALL
|
||||
postgres=> update public.profiles set updated_at=now();
|
||||
UPDATE 3 -- the 'postgres' role can update and see all profiles
|
||||
postgres=> table public.profiles;
|
||||
id | updated_at | username | full_name | avatar_url | website
|
||||
--------------------------------------+-------------------------------+----------+-----------+------------+---------
|
||||
15d6811a-16ee-4fa2-9b18-b63085688be4 | 2023-02-18 21:40:08.216324+00 | user1 | User 1 | |
|
||||
4e1010bb-eb37-4a4d-a05a-b0ee315c9d56 | 2023-02-18 21:40:08.216324+00 | user2 | User 2 | |
|
||||
d4f0aa86-e6f6-41d1-bd32-391f077cf1b9 | 2023-02-18 21:40:08.216324+00 | user3 | User 3 | |
|
||||
(3 rows)
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Deprecated features
|
||||
|
||||
We have deprecate some functions to ensure better performance and extensibilty of RLS policies.
|
||||
|
||||
@@ -160,6 +160,12 @@ use this potentially stale information to render a page.
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
### No session on the server side with Next.js route prefetching?
|
||||
|
||||
When you use route prefetching in Next.js using `<Link href="/...">` components or the `Router.push()` APIs can send server-side requests before the browser processes the access and refresh tokens. This means that those requests may not have any cookies set and your server code will render unauthenticated content.
|
||||
|
||||
To improve experience for your users, we recommend redirecting users to one specific page after sign-in that does not include any route prefetching from Next.js. Once the Supabase client library running in the browser has obtained the access and refresh tokens from the URL fragment, you can send users to any pages that use prefetching.
|
||||
|
||||
### How do I make the cookies `HttpOnly`?
|
||||
|
||||
This is not necessary. Both the access token and refresh token are designed to
|
||||
|
||||
@@ -178,9 +178,9 @@ This creates a new migration named `supabase/migrations/<timestamp>_create_emplo
|
||||
Use the seed script in `supabase/seed.sql` (created with [`supabase init`](/docs/reference/cli/usage#supabase-init)) to add sample data to the table.
|
||||
|
||||
```sql
|
||||
-- in supabase/seed.sql
|
||||
insert into
|
||||
public.employees (name)
|
||||
-- in supabase/seed.sql
|
||||
insert into public.employees
|
||||
(name)
|
||||
values
|
||||
('Erlich Bachman'),
|
||||
('Richard Hendricks'),
|
||||
@@ -200,9 +200,9 @@ You should now see the contents of `employees` in Studio.
|
||||
Use the [`reset`](/docs/reference/cli/usage#supabase-db-reset) command to revert any changes to the local database.
|
||||
|
||||
```sql
|
||||
-- run on local database to make a change
|
||||
alter table
|
||||
employees add department text default 'Hooli';
|
||||
-- run on local database to make a change
|
||||
alter table employees
|
||||
add department text default 'Hooli';
|
||||
```
|
||||
|
||||
Run the following command to reset the local database:
|
||||
|
||||
@@ -390,7 +390,7 @@ This ensures that any `delete()` or `update()` would fail if there are no accomp
|
||||
To confirm that safeupdate is enabled for queries going through the API of your project, the following query could be run:
|
||||
|
||||
```sql
|
||||
select usename,useconfig from pg_shadow where usename = 'authenticator' ;
|
||||
select usename, useconfig from pg_shadow where usename = 'authenticator';
|
||||
```
|
||||
|
||||
The expected value for `useconfig` should be:
|
||||
|
||||
@@ -30,7 +30,10 @@ Create a test table with a text array (an array of strings):
|
||||
<TabPanel id="sql" label="SQL">
|
||||
|
||||
```sql
|
||||
CREATE TABLE arraytest (id integer NOT NULL, textarray text ARRAY);
|
||||
create table arraytest (
|
||||
id integer not null,
|
||||
textarray text array
|
||||
);
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
@@ -95,7 +98,7 @@ You should see:
|
||||
<TabPanel id="sql" label="SQL">
|
||||
|
||||
```sql
|
||||
SELECT * FROM arraytest;
|
||||
select * from arraytest;
|
||||
```
|
||||
|
||||
You should see:
|
||||
|
||||
@@ -76,6 +76,7 @@ The following table shows a subset of available columns:
|
||||
A full list of statistics is available in the [pg_stat_monitor docs](https://docs.percona.com/pg-stat-monitor/reference.html#postgresql-15).
|
||||
|
||||
## Functions
|
||||
|
||||
- [`pg_stat_monitor_reset()`](https://docs.percona.com/pg-stat-monitor/functions.html): Resets the statistics tracked by the `pg_stat_monitor` view and deletes all previous data.
|
||||
- [`pg_stat_monitor_version()`](https://docs.percona.com/pg-stat-monitor/functions.html): Displays the version of the `pg_stat_monitor` extension.
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ select cron.schedule (
|
||||
Vacuum every day at 3:00am (GMT)
|
||||
|
||||
```sql
|
||||
SELECT cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM');
|
||||
select cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM');
|
||||
```
|
||||
|
||||
### Invoke Supabase Edge Function every minute
|
||||
@@ -112,7 +112,7 @@ select
|
||||
Unschedules a job called `'nightly-vacuum'`
|
||||
|
||||
```sql
|
||||
SELECT cron.unschedule('nightly-vacuum');
|
||||
select cron.unschedule('nightly-vacuum');
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
@@ -16,7 +16,6 @@ The pg_net API is in alpha. Functions signatures may change.
|
||||
|
||||
It differs from the `http` extension in that it is asynchronous by default. This makes it useful in blocking functions (like triggers).
|
||||
|
||||
|
||||
## Enable the extension
|
||||
|
||||
<Tabs
|
||||
|
||||
@@ -8,7 +8,6 @@ export const meta = {
|
||||
|
||||
`PGroonga` is a PostgreSQL extension adding a full text search indexing method based on [Groonga](https://groonga.org). While native PostgreSQL supports full text indexing, it is limited to alphabet and digit based languages. `PGroonga` offers a wider range of character support making it viable for a superset of languages supported by PostgreSQL including Japanese, Chinese, etc.
|
||||
|
||||
|
||||
## Enable the extension
|
||||
|
||||
<Tabs
|
||||
|
||||
@@ -60,13 +60,12 @@ To disable an extension, call `drop extension`.
|
||||
### Create a table to store vectors
|
||||
|
||||
```sql
|
||||
create table
|
||||
posts (
|
||||
id serial primary key,
|
||||
title text not null,
|
||||
body text not null,
|
||||
embedding vector (1536)
|
||||
);
|
||||
create table posts (
|
||||
id serial primary key,
|
||||
title text not null,
|
||||
body text not null,
|
||||
embedding vector(1536)
|
||||
);
|
||||
```
|
||||
|
||||
### Storing a vector / embedding
|
||||
|
||||
@@ -15,7 +15,6 @@ While Postgres natively runs SQL, it can also run other "procedural languages".
|
||||
|
||||
It can be used for database functions, triggers, queries and more.
|
||||
|
||||
|
||||
## Enable the extension
|
||||
|
||||
<Tabs
|
||||
|
||||
@@ -91,7 +91,7 @@ You can insert geographical data through SQL or through our API.
|
||||
|
||||
```sql
|
||||
insert into public.restaurants
|
||||
(name, location)
|
||||
(name, location)
|
||||
values
|
||||
('Supa Burger', st_point(-73.946823, 40.807416)),
|
||||
('Supa Pizza', st_point(-73.94581, 40.807475)),
|
||||
|
||||
@@ -6,7 +6,7 @@ export const meta = {
|
||||
description: '3rd party integrations for PostgreSQL ',
|
||||
}
|
||||
|
||||
[supabase/wrappers](https://supabase.github.io/wrappers/) is a PostgreSQL extension that provides integrations with external sources so you can interact with third-party data using SQL.
|
||||
[supabase/wrappers](https://supabase.github.io/wrappers/) is a PostgreSQL extension that provides integrations with external sources so you can interact with third-party data using SQL.
|
||||
|
||||
For example, the [Stripe wrapper](https://supabase.github.io/wrappers/stripe/) connects to [Stripe's API](https://stripe.com/docs/api) and exposes each endpoint as a SQL table.
|
||||
|
||||
|
||||
@@ -49,13 +49,26 @@ create table books (
|
||||
description text
|
||||
);
|
||||
|
||||
insert into books (title, author, description)
|
||||
insert into books
|
||||
(title, author, description)
|
||||
values
|
||||
('The Poky Little Puppy','Janette Sebring Lowrey','Puppy is slower than other, bigger animals.'),
|
||||
('The Tale of Peter Rabbit','Beatrix Potter','Rabbit eats some vegetables.'),
|
||||
('Tootle','Gertrude Crampton','Little toy train has big dreams.'),
|
||||
('Green Eggs and Ham','Dr. Seuss','Sam has changing food preferences and eats unusually colored food.'),
|
||||
('Harry Potter and the Goblet of Fire','J.K. Rowling','Fourth year of school starts, big drama ensues.');
|
||||
(
|
||||
'The Poky Little Puppy',
|
||||
'Janette Sebring Lowrey',
|
||||
'Puppy is slower than other, bigger animals.'
|
||||
),
|
||||
('The Tale of Peter Rabbit', 'Beatrix Potter', 'Rabbit eats some vegetables.'),
|
||||
('Tootle', 'Gertrude Crampton', 'Little toy train has big dreams.'),
|
||||
(
|
||||
'Green Eggs and Ham',
|
||||
'Dr. Seuss',
|
||||
'Sam has changing food preferences and eats unusually colored food.'
|
||||
),
|
||||
(
|
||||
'Harry Potter and the Goblet of Fire',
|
||||
'J.K. Rowling',
|
||||
'Fourth year of school starts, big drama ensues.'
|
||||
);
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
@@ -70,8 +83,7 @@ The functions we'll cover in this guide are:
|
||||
Converts your data into searchable "tokens". `to_tsvector()` stands for "to text search vector". For example:
|
||||
|
||||
```sql
|
||||
select to_tsvector('green eggs and ham')
|
||||
|
||||
select to_tsvector('green eggs and ham');
|
||||
-- Returns 'egg':2 'green':1 'ham':4
|
||||
```
|
||||
|
||||
|
||||
@@ -138,7 +138,8 @@ create table planets (
|
||||
name text
|
||||
);
|
||||
|
||||
insert into planets (id, name)
|
||||
insert into planets
|
||||
(id, name)
|
||||
values
|
||||
(1, 'Tattoine'),
|
||||
(2, 'Alderaan'),
|
||||
@@ -150,7 +151,8 @@ create table people (
|
||||
planet_id bigint references planets
|
||||
);
|
||||
|
||||
insert into people (id, name, planet_id)
|
||||
insert into people
|
||||
(id, name, planet_id)
|
||||
values
|
||||
(1, 'Anakin Skywalker', 1),
|
||||
(2, 'Luke Skywalker', 1),
|
||||
|
||||
@@ -221,12 +221,11 @@ Select the title, description, price, and age range for each book.
|
||||
```sql
|
||||
select
|
||||
title,
|
||||
metadata -> 'description' AS description,
|
||||
metadata -> 'description' as description,
|
||||
metadata -> 'price' as price,
|
||||
metadata -> 'ages' -> 0 as low_age,
|
||||
metadata -> 'ages' -> 1 as high_age
|
||||
from
|
||||
books;
|
||||
from books;
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
@@ -12,7 +12,7 @@ Run the following query using the [SQL Editor](https://app.supabase.com/project/
|
||||
|
||||
```sql
|
||||
select
|
||||
version ();
|
||||
version();
|
||||
```
|
||||
|
||||
Which should return something like:
|
||||
@@ -11,10 +11,11 @@ export const meta = {
|
||||
Select a set of columns from a single table with where, order by, and limit clauses.
|
||||
|
||||
```sql
|
||||
select first_name, last_name, team_id, age from players
|
||||
select first_name, last_name, team_id, age
|
||||
from players
|
||||
where age between 20 and 24 and team_id <> 'STL'
|
||||
order by last_name, first_name desc
|
||||
limit 20
|
||||
limit 20;
|
||||
```
|
||||
|
||||
```js
|
||||
@@ -32,8 +33,9 @@ const { data, error } = await supabase
|
||||
Select all columns from a single table with a complex where clause: OR AND OR
|
||||
|
||||
```sql
|
||||
select * from players
|
||||
where ((team_id = 'CHN' or team_id is null) and (age > 35 or age is null))
|
||||
select *
|
||||
from players
|
||||
where ((team_id = 'CHN' or team_id is null) and (age > 35 or age is null));
|
||||
```
|
||||
|
||||
```js
|
||||
@@ -48,8 +50,9 @@ const { data, error } = await supabase
|
||||
Select all columns from a single table with a complex where clause: AND OR AND
|
||||
|
||||
```sql
|
||||
select * from players
|
||||
where ((team_id = 'CHN' and age > 35) or (team_id <> 'CHN' and age is not null))
|
||||
select *
|
||||
from players
|
||||
where ((team_id = 'CHN' and age > 35) or (team_id <> 'CHN' and age is not null));
|
||||
```
|
||||
|
||||
```js
|
||||
@@ -62,8 +65,9 @@ const { data, error } = await supabase
|
||||
Get a count of rows, but don't return any data.
|
||||
|
||||
```sql
|
||||
select count(*) from players
|
||||
where team_id = 'NYM'
|
||||
select count(*)
|
||||
from players
|
||||
where team_id = 'NYM';
|
||||
```
|
||||
|
||||
```js
|
||||
|
||||
@@ -189,8 +189,14 @@ Use the "Bulk Loading" instructions if you are loading large data sets.
|
||||
insert into movies
|
||||
(name, description)
|
||||
values
|
||||
('The Empire Strikes Back', 'After the Rebels are brutally overpowered by the Empire on the ice planet Hoth, Luke Skywalker begins Jedi training with Yoda.'),
|
||||
('Return of the Jedi', 'After a daring mission to rescue Han Solo from Jabba the Hutt, the Rebels dispatch to Endor to destroy the second Death Star.');
|
||||
(
|
||||
'The Empire Strikes Back',
|
||||
'After the Rebels are brutally overpowered by the Empire on the ice planet Hoth, Luke Skywalker begins Jedi training with Yoda.'
|
||||
),
|
||||
(
|
||||
'Return of the Jedi',
|
||||
'After a daring mission to rescue Han Solo from Jabba the Hutt, the Rebels dispatch to Endor to destroy the second Death Star.'
|
||||
);
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
@@ -425,14 +431,15 @@ As a query becomes complex it becomes a hassle to call it. Especially when we ru
|
||||
|
||||
```sql
|
||||
select
|
||||
students.name,
|
||||
students.type,
|
||||
courses.title,
|
||||
courses.code,
|
||||
grades.result
|
||||
from grades
|
||||
left join students on grades.student_id = students.id
|
||||
left join courses on grades.course_id = courses.id;
|
||||
students.name,
|
||||
students.type,
|
||||
courses.title,
|
||||
courses.code,
|
||||
grades.result
|
||||
from
|
||||
grades
|
||||
left join students on grades.student_id = students.id
|
||||
left join courses on grades.course_id = courses.id;
|
||||
```
|
||||
|
||||
We can run this instead:
|
||||
@@ -449,14 +456,15 @@ Views ensure that the likelihood of mistakes decreases when repeatedly executing
|
||||
|
||||
```sql
|
||||
select
|
||||
students.name,
|
||||
students.type,
|
||||
courses.title,
|
||||
courses.code,
|
||||
grades.result
|
||||
from grades
|
||||
left join students on grades.student_id = students.id
|
||||
left join courses on grades.course_id = courses.id
|
||||
students.name,
|
||||
students.type,
|
||||
courses.title,
|
||||
courses.code,
|
||||
grades.result
|
||||
from
|
||||
grades
|
||||
left join students on grades.student_id = students.id
|
||||
left join courses on grades.course_id = courses.id
|
||||
where courses.code != 'PG101';
|
||||
```
|
||||
|
||||
@@ -478,13 +486,14 @@ Using our example above, a materialized view can be created like this:
|
||||
|
||||
```sql
|
||||
create materialized view transcripts as
|
||||
select
|
||||
students.name,
|
||||
students.type,
|
||||
courses.title,
|
||||
courses.code,
|
||||
grades.result
|
||||
from grades
|
||||
select
|
||||
students.name,
|
||||
students.type,
|
||||
courses.title,
|
||||
courses.code,
|
||||
grades.result
|
||||
from
|
||||
grades
|
||||
left join students on grades.student_id = students.id
|
||||
left join courses on grades.course_id = courses.id;
|
||||
```
|
||||
|
||||
@@ -16,7 +16,7 @@ export const meta = {
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
[Clouflare Turnstile](https://www.cloudflare.com/products/turnstile/) is a friendly, free CAPTCHA replacement, and it works seamlessly with Supabase Edge Functions to protect your forms. [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/cloudflare-turnstile).
|
||||
[Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/) is a friendly, free CAPTCHA replacement, and it works seamlessly with Supabase Edge Functions to protect your forms. [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/cloudflare-turnstile).
|
||||
|
||||
## Setup
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@ export const meta = {
|
||||
Run your app on a platform of your choosing! By default an app should launch in your web browser.
|
||||
|
||||
Note that `supabase_flutter` is compatible with web, iOS, Android, macOS, and Windows apps.
|
||||
Running the app on MacOS requires additional configuration to [set the entitlements](https://docs.flutter.dev/development/platform-integration/macos/building#setting-up-entitlements).
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ Supabase offers a variety of options for populating tables with data, including
|
||||
|
||||
Fill out the info in the table. The database is now set up.
|
||||
|
||||
### Step 2: Build UI on ILLA Builder
|
||||
### Step 2: Build UI on ILLA Cloud
|
||||
|
||||
On [ILLA Builder](https://fast-try.illacloud.com/), click Create New to create a new application.
|
||||
On [ILLA Cloud](https://cloud.illacloud.com/), click Create New to create a new application.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ This guide explains how to quickly connect the Postgres database provided by Sup
|
||||
## Step 1: Get the connection string from Supabase project settings
|
||||
|
||||
Go to the settings page from the sidebar and navigate to the **Database** tab. You’ll find the database’s connection string with a placeholder for the password you provided when you created the project.
|
||||
|
||||

|
||||
|
||||
## Step 2: Testing the connection
|
||||
@@ -29,22 +30,17 @@ In case you don’t have a Prisma project or this is your first time working wit
|
||||
|
||||
### Cloning the starter project
|
||||
|
||||
Navigate into a directory of your choice and run the following command in your terminal if you’re on a Windows machine:
|
||||
Navigate into a directory of your choice and run the following command in your terminal:
|
||||
|
||||
```bash
|
||||
curl https://pris.ly/quickstart -L -o quickstart-main.tar.gz && tar -zxvf quickstart-main.tar.gz quickstart-main/typescript/starter && move quickstart-main\typescript\starter starter && rmdir /S /Q quickstart-main && del /Q quickstart-main.tar.gz
|
||||
```
|
||||
|
||||
And if you’re using Mac OS or Linux, run the following command:
|
||||
|
||||
```bash
|
||||
curl -L https://pris.ly/quickstart | tar -xz --strip=2 quickstart-main/typescript/starter
|
||||
curl https://codeload.github.com/prisma/prisma-examples/tar.gz/latest | tar -xz --strip=2 prisma-examples-latest/databases/postgresql-supabase
|
||||
```
|
||||
|
||||
You can now navigate into the directory and install the project’s dependencies:
|
||||
|
||||
```bash
|
||||
cd starter && npm install
|
||||
cd postgresql-supabase
|
||||
npm install
|
||||
```
|
||||
|
||||
### A look at the project’s structure
|
||||
@@ -52,53 +48,43 @@ cd starter && npm install
|
||||
This project comes with TypeScript configured and has the following structure.
|
||||
|
||||
- A `prisma` directory which contains:
|
||||
- A `dev.db` file: This is a SQLite database.
|
||||
- A `schema.prisma` file: Where we define the different database models and relations between them.
|
||||
- A `.env` file: Contains the `DATABASE_URL` variable, which Prisma will use.
|
||||
- A `script.ts` file: where we will run some queries using Prisma Client.
|
||||
This starter also comes with the following packages installed:
|
||||
- A `seed.ts` file: This is the data used to seed your database.
|
||||
- A `schema.prisma` file: Where you define the different database models and relations between them.
|
||||
- A `script.ts` file: where you will run some queries using Prisma Client.
|
||||
|
||||
This starter also comes with the following packages installed:
|
||||
- [`@prisma/client`](https://www.npmjs.com/package/@prisma/client): An auto-generated and type-safe query builder that’s _tailored_ to your data.
|
||||
- [`prisma`](https://www.npmjs.com/package/prisma): Prisma’s command-line interface (CLI). It allows you to initialize new project assets, generate Prisma Client, and analyze existing database structures through introspection to automatically create your application models.
|
||||
> Note: Prisma works with both JavaScript and TypeScript. However, to get the best possible development experience, using TypeScript is highly recommended.
|
||||
|
||||
### Configuring the project to use PostgreSQL
|
||||
> Note: Prisma works with both JavaScript and TypeScript. However, to get the best possible development experience, using TypeScript is highly recommended.
|
||||
|
||||
By default, Prisma migrations will try to drop the `postgres` database, which can lead to conflicts with Supabase databases. For this scenario, use [Prisma Shadow Databases](https://www.prisma.io/docs/concepts/components/prisma-migrate/shadow-database#cloud-hosted-shadow-databases-must-be-created-manually).
|
||||
### Configuring the project
|
||||
|
||||
Create a shadow database in your PostgreSQL server within the same Supabase project using the `psql` CLI and the `DATABASE_URL` from the previous steps (or use the local database).
|
||||
Create a `.env` file at the root of your project:
|
||||
|
||||
```bash
|
||||
psql postgresql://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432
|
||||
touch .env
|
||||
```
|
||||
|
||||
After you connect to your project's PostgreSQL instance, create another database (e.g., `postgres_shadow`):
|
||||
|
||||
```bash
|
||||
postgres=> CREATE DATABASE postgres_shadow;
|
||||
postgres=> exit
|
||||
```
|
||||
|
||||
Go ahead and delete the `prisma/dev.db` file because we will be switching to PostgreSQL.
|
||||
In the `.env` file, update `DATABASE_URL` and `SHADOW_DATABASE_URL` to the connection string from **step 1**. The `.env` file should look like:
|
||||
In the `.env` file, add a `DATABASE_URL` variable and add the connection string from **step 1**. The `.env` file should look like:
|
||||
|
||||
```env
|
||||
# .env
|
||||
DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres"
|
||||
SHADOW_DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres_shadow"
|
||||
```
|
||||
|
||||
In the `schema.prisma` file, change the `provider` from "sqlite" to `"postgresql"` and add the `shadowDatabaseUrl` property.
|
||||
This is what your `schema.prisma` file should look like:
|
||||
|
||||
```go
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
model Post {
|
||||
id Int @id @default(autoincrement())
|
||||
title String
|
||||
@@ -107,6 +93,7 @@ model Post {
|
||||
author User? @relation(fields: [authorId], references: [id])
|
||||
authorId Int?
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique
|
||||
@@ -115,53 +102,81 @@ model User {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
To test that everything works correctly, run the following command to create a migration:
|
||||
|
||||
```bash
|
||||
prisma migrate dev --name init
|
||||
npx prisma migrate dev --name init
|
||||
```
|
||||
|
||||
You can optionally give your migration a name, depending on the changes you made. Since this is the project’s first migration, you’re setting the `--name` flag to “init”.
|
||||
If everything works correctly, you should get the following message in your terminal:
|
||||
You can optionally give your migration a name, depending on the changes you made. Since this is the project’s first migration, you’re setting the `--name` flag to “init”. If everything works correctly, you should get the following message in your terminal:
|
||||
|
||||
```text
|
||||
Your database is now in sync with your schema.
|
||||
:heavy_check_mark: Generated Prisma Client (2.x.x) to ./node_modules/@prisma/client in 111ms
|
||||
:heavy_check_mark: Generated Prisma Client (4.x.x) to ./node_modules/@prisma/client in 111ms
|
||||
```
|
||||
|
||||
This will create a `prisma/migrations` folder inside your `prisma` directory and synchronize your Prisma schema with your database schema.
|
||||
|
||||
> Note: if you want to skip the process of creating a migration history, you can use the [`db push`](https://www.prisma.io/docs/concepts/components/prisma-migrate/db-push) command instead of `migrate dev`.
|
||||
> If you go to your Supabase project, in the table editor, you should see that two tables have been created, a `Post` and a `User` table.
|
||||
> 
|
||||
> That’s it! You have now successfully connected a Prisma project to a PostgreSQL database hosted on Supabase and ran your first migration.
|
||||
> **Note**: If you want to skip the process of creating a migration history, you can use the [`prisma db push`](https://www.prisma.io/docs/concepts/components/prisma-migrate/db-push) command instead of `prisma migrate dev`. However, we recommend using `prisma migrate dev` to evolve your database schema in development.
|
||||
> If you would like to get a conceptual overview of how Prisma Migrate works and which commands to use in what environment, refer to [this page in the Prisma documentation](https://www.prisma.io/docs/concepts/components/prisma-migrate/mental-model).
|
||||
|
||||
If you go to your Supabase project, in the table editor, you should see that two tables have been created, a `Post`, `User`, and `_prisma_migrations` tables. The `_prisma_migrations` table is used to keep
|
||||
|
||||

|
||||
|
||||
That’s it! You have now successfully connected a Prisma project to a PostgreSQL database hosted on Supabase and ran your first migration.
|
||||
|
||||
## Connection pooling with Supabase
|
||||
|
||||
If you’re working in a serverless environment (for example Node.js functions hosted on AWS Lambda, Vercel or Netlify Functions), you need to set up [connection pooling](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#serverless-environments-faas) using a tool like [PgBouncer](https://www.pgbouncer.org/). That’s because every function invocation may result in a [new connection to the database](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#the-serverless-challenge). Supabase [supports connection management using PgBouncer](https://supabase.io/blog/2021/04/02/supabase-pgbouncer#what-is-connection-pooling) and are enabled by default.
|
||||
Go to the **Database** page from the sidebar in the Supabase dashboard and navigate to **connection pool** settings
|
||||
If you’re working in a serverless environment (for example Node.js functions hosted on AWS Lambda, Vercel or Netlify Functions), you need to set up [connection pooling](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#serverless-environments-faas) using a tool like [PgBouncer](https://www.pgbouncer.org/). That’s because every function invocation may result in a [new connection to the database](https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#the-serverless-challenge).
|
||||
|
||||
Supabase [supports connection management using PgBouncer](/docs/guides/database/connecting-to-postgres#connection-pool) which prevents a traffic spike from overwhelming your database.
|
||||
|
||||
Go to the **Database** page from the sidebar in the Supabase dashboard and navigate to **Connection pool** settings:
|
||||
|
||||

|
||||
When migrating, you need to use the non-pooled connection URL (like the one used in **step 1**). However, when deploying your app, use the pooled connection URL and add the `?pgbouncer=true` flag to the PostgreSQL connection URL. It's also recommended to minimize the number of concurrent connections by setting the `connection_limit` to `1`. The `.env` file should look like:
|
||||
|
||||
When updating your database schema, you need to use the non-pooled connection URL (like the one used in **step 1**). You can configure the non-pooled connection string by using the `directUrl` property in the datasource block.
|
||||
|
||||
Update your `.env` file with the following changes:
|
||||
1. Rename the `DATABASE_URL` environment variable to `DIRECT_URL`
|
||||
1. Create a `DATABASE_URL` environment variable and paste in the new connection string from the dashboard as its value
|
||||
|
||||
Append the `?pgbouncer=true` flag to the `DATABASE_URL` variable.
|
||||
|
||||
Your `.env` file should resemble the following:
|
||||
|
||||
```env
|
||||
# .env
|
||||
DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:6543/postgres?pgbouncer=true&connection_limit=1"
|
||||
SHADOW_DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres_shadow"
|
||||
# PostgreSQL connection string used for migrations
|
||||
DIRECT_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres"
|
||||
# PostgreSQL connection string with pgBouncer config — used by Prisma Client
|
||||
DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:6543/postgres?pgbouncer=true"
|
||||
|
||||
```
|
||||
|
||||
Prisma Migrate uses database transactions to check out the current state of the database and the migrations table. However, the Migration Engine is designed to use a single connection to the database, and does not support connection pooling with PgBouncer. If you attempt to run Prisma Migrate commands in any environment that uses PgBouncer for connection pooling, you might see the following error:
|
||||
Update your Prisma schema by setting the `directUrl` in the datasource block:
|
||||
|
||||
```bash
|
||||
Error: undefined: Database error
|
||||
Error querying the database: db error: ERROR: prepared statement “s0” already exists
|
||||
```go
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
directURL = env("DIRECT_URL")
|
||||
}
|
||||
```
|
||||
|
||||
This is a known issue and it is being worked on, you can follow the progress on this [GitHub issue](https://github.com/prisma/prisma/issues/6485).
|
||||
If you want to learn more about Prisma, check out the [docs](https://www.prisma.io/docs). Also in case you have any questions or run into any issue, feel free to start a discussion in the repo’s [discussions section](https://github.com/prisma/prisma/discussions).
|
||||
> **Note**: This feature is available from Prisma version [4.10.0](https://github.com/prisma/prisma/releases/tag/4.10.0) and higher.
|
||||
|
||||
If you want to learn more about Prisma, check out the [docs](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#fields). Also in case you have any questions or run into any issue, feel free to start a discussion in the repo’s [discussions section](https://github.com/prisma/prisma/discussions).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you run `prisma migrate dev --name init` multiple times, it sometimes asks if you want to recreate the whole schema. If you chose yes, it will delete the public schema and recreate it. The default grants are missing after this. If you run into this problem, add a helper SQL for fixing the grants:
|
||||
### Missing grants
|
||||
|
||||
If your database schema is out of sync from your migration history, `prisma migrate dev` will detect a migration history conflict or a [schema drift](https://www.prisma.io/docs/guides/database/developing-with-prisma-migrate/troubleshooting-development#schema-drift). When `prisma migrate dev` detects the drift, it might ask to to reset your database schema. If you choose yes, it will delete the `public` schema along with the default grants defined in your database.
|
||||
|
||||
If you run into this problem, create a draft migration using `prisma migrate dev --create-only`, and add the following helper SQL:
|
||||
|
||||
```sql
|
||||
grant usage on schema public to postgres, anon, authenticated, service_role;
|
||||
@@ -175,6 +190,98 @@ alter default privileges in schema public grant all on functions to postgres, an
|
||||
alter default privileges in schema public grant all on sequences to postgres, anon, authenticated, service_role;
|
||||
```
|
||||
|
||||
Run `prisma migrate dev` to apply the draft migration to the database.
|
||||
|
||||
### Using Supabase Auth with Prisma
|
||||
|
||||
If you would like to use Supabase Auth and Prisma in your application, you will have to enable the `multiSchema` Preview feature flag in the `generator` block of your Prisma schema:
|
||||
|
||||
```go
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
directURL = env("DIRECT_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["multiSchema"]
|
||||
}
|
||||
```
|
||||
|
||||
Next, specify the database schemas you would like to include in your Prisma schema:
|
||||
|
||||
```go
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
directURL = env("DIRECT_URL")
|
||||
schemas = ["public", "auth"]
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["multiSchema"]
|
||||
}
|
||||
```
|
||||
|
||||
You can then specify what schema a model or enum belongs to using the `@@schema` attribute:
|
||||
|
||||
```go
|
||||
model User {
|
||||
id Int @id
|
||||
// ...
|
||||
|
||||
@@schema("auth") // or @@schema("public")
|
||||
}
|
||||
```
|
||||
|
||||
To learn more about using Prisma with multiple database schemas, refer to [this page in the Prisma docs](https://www.prisma.io/docs/guides/database/multi-schema#learn-more-about-the-multischema-preview-feature).
|
||||
|
||||
### Using PostgreSQL Row Level Security with Prisma
|
||||
|
||||
|
||||
If you would like to use Row Level Security (RLS) with Prisma, check out the [Prisma Client Extension - Row Level Security example](https://github.com/prisma/prisma-client-extensions/tree/main/row-level-security) that provides the primitives you could use to build and extend Prisma Client in PostgreSQL.
|
||||
|
||||
Also check out [useSupabaseRowLevelSecurity](https://github.com/dthyresson/prisma-extension-supabase-rls) Prisma Client extension that supports [Supabase RLS](/docs/guides/auth/row-level-security#authrole) and policies written to use [Supabase auth](/docs/guides/auth/overview).
|
||||
|
||||
The example and extension use [Prisma Client extensions](https://www.prisma.io/docs/concepts/components/prisma-client/client-extensions) Preview feature.
|
||||
|
||||
### Enabling PosgreSQL extensions
|
||||
|
||||
If you would like to use a PostgreSQL extension with Prisma, enable the `postgresqlExtensions` Preview feature flag in the `generator` block of your Prisma schema:
|
||||
|
||||
```go
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
directURL = env("DIRECT_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["postgresqlExtensions"]
|
||||
}
|
||||
```
|
||||
|
||||
Next, specify the extensions you need in the `datasource` block:
|
||||
|
||||
```go
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
directURL = env("DIRECT_URL")
|
||||
extensions = [hstore(schema: "myHstoreSchema"), pg_trgm, postgis(version: "2.1")]
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["postgresqlExtensions"]
|
||||
}
|
||||
```
|
||||
|
||||
To learn more about using Prisma with PostgreSQL extensions, refer to [this page in the Prisma docs](https://www.prisma.io/docs/concepts/components/prisma-schema/postgresql-extensions).
|
||||
|
||||
## Resources
|
||||
|
||||
- [Prisma](https://prisma.io) official website.
|
||||
|
||||
@@ -155,6 +155,7 @@ As with the final activation stage of the process for setting up a vanity subdom
|
||||
|
||||
- Edge functions do not honor the custom domain or the vanity subdomain setting and they still have to be invoked via the `foobarbaz.supabase.co` domain.
|
||||
- A Supabase project can—at this time—use either a Custom Domain or a Vanity Subdomain, but not both.
|
||||
- Some authentication flows like Sign-in with Twitter set cookies to track the progress of the flow. Make sure you use only one domain in your frontend application for this reason. Mixing calls to the Supabase domain `foobarbaz.supabase.co` and your custom domain could cause those flows to stop working due to the [Same Origin Policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) enforced on cookies by the browser.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'database-size',
|
||||
title: 'Database size',
|
||||
description: 'Understanding how database size applies to your subscription.',
|
||||
}
|
||||
|
||||
Database size refers to the _monthly average storage usage_, as reported by Postgres. This metric is reported in your project's [billing usage](https://app.supabase.com/project/_/settings/billing/usage) and is updated daily. As you read this document we will refer to "database size" and "disk size":
|
||||
|
||||
- "Database size" is the total size of used storage from your database.
|
||||
- "Disk size" describes the size of the underlying available storage.
|
||||
|
||||
## Database space management
|
||||
|
||||
### Database size
|
||||
|
||||
This SQL query will show the current size of your Postgres database:
|
||||
|
||||
```sql
|
||||
select
|
||||
sum(pg_database_size(pg_database.datname)) / (1024 * 1024) as db_size_mb
|
||||
from pg_database;
|
||||
```
|
||||
|
||||
This value is reported in the [database settings page](https://app.supabase.com/project/_/settings/database).
|
||||
|
||||
Database Space is consumed primarily by your data, indexes, and materialized views. You can reduce your disk size by removing any of these and running a Vacuum operation.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
Depending on your billing tier, your database can go into read-only mode which can prevent you inserting and deleting data. There are instructions for managing read-only mode in the [Disk Management](#disk-management) section.
|
||||
|
||||
</Admonition>
|
||||
|
||||
### Vacuum operations
|
||||
|
||||
Postgres does not immediately reclaim the physical space used by dead tuples (i.e., deleted rows) in the DB. They are marked as "removed" until a [vacuum operation](https://www.postgresql.org/docs/current/routine-vacuuming.html) is executed. As a result, deleting data from your database may not immediately reduce the reported disk usage.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
Vacuum operations can temporarily increase resource utilization, which may adversely impact the observed performance of your project until the maintenance is completed.
|
||||
|
||||
</Admonition>
|
||||
|
||||
Supabase projects have automatic vacuuming enabled, which ensures that these operations are performed regularly to keep the database healthy and performant.
|
||||
It is possible to [fine-tune](https://www.percona.com/blog/2018/08/10/tuning-autovacuum-in-postgresql-and-autovacuum-internals/)
|
||||
the [autovacuum parameters](https://www.enterprisedb.com/blog/postgresql-vacuum-and-analyze-best-practice-tips),
|
||||
or [manually initiate](https://www.postgresql.org/docs/current/sql-vacuum.html) vacuum operations.
|
||||
Running a manual vacuum after deleting large amounts of data from your DB could help reduce the database size reported by Postgres.
|
||||
|
||||
### Preoccupied Space
|
||||
|
||||
New Supabase projects have a database size of ~40-60mb. This space includes pre-installed extensions, schemas, and default Postgres data. Additional database size is used when installing extensions, even if those extensions are inactive.
|
||||
|
||||
## Disk management
|
||||
|
||||
Supabase uses network-attached storage to balance performance with scalability. The behavior of your disk depends on your billing tier.
|
||||
|
||||
### Paid Tier Behavior
|
||||
|
||||
Pro and Enterprise projects have auto-scaling Disk Storage.
|
||||
|
||||
Disk storage expands automatically when the database reaches 90% of the disk size. The disk is expanded to be 50% larger (e.g., 8GB -> 12GB). Auto-scaling can only take place once every 6 hours. If within those 6 hours you reach 95% of the disk space, your project will enter read-only mode.
|
||||
|
||||
<Admonition type="caution">
|
||||
|
||||
If you intend to import a lot of data into your database which requires multiple disk expansions then [reach out to our team](https://app.supabase.com/support/new). For example, uploading more than 1.5x the current size of your database storage will put your database into [read-only mode](#read-only-mode).
|
||||
|
||||
</Admonition>
|
||||
|
||||
The maximum Disk Size for Pro Tier is 1024TB. If you need more than this, [contact us](https://app.supabase.com/support/new) to learn more about the Enterprise plan.
|
||||
|
||||
### Free Tier Behavior
|
||||
|
||||
Free Tier projects enter [read-only](#read-only-mode) mode when you exceed the 500mb limit. Once in read-only mode, you have several options:
|
||||
|
||||
- [Upgrade to the Pro or Enterprise tier](https://app.supabase.com/project/_/settings/billing/subscription) to enable auto-scaling and expand beyond the 500mb database size limit.
|
||||
- [Disable read-only mode](#disabling-read-only-mode) and reduce your database size.
|
||||
|
||||
### Read-only mode
|
||||
|
||||
In some cases Supabase may put your database into read-only mode to prevent your database from exceeding the billing or disk limitations.
|
||||
|
||||
In read-only mode, clients will encounter errors such as `cannot execute INSERT in a read-only transaction`. Regular operation (read-write mode) is automatically re-enabled once usage is below 95% of the disk size,
|
||||
|
||||
### Disabling read-only mode
|
||||
|
||||
You can manually override read-only mode to reduce disk size. To do this, run the following in the [SQL Editor](https://app.supabase.com/project/_/sql):
|
||||
|
||||
```sql
|
||||
SET
|
||||
default_transaction_read_only = 'off';
|
||||
```
|
||||
|
||||
This allows you to delete data from within the session. After deleting data, you should run a vacuum to reclaim as much space as possible.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -1,74 +0,0 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'database-usage',
|
||||
title: 'Database usage',
|
||||
description: 'Understanding how database usage applies to your subscription.',
|
||||
}
|
||||
|
||||
Database size refers to the _monthly average storage usage_, as reported by Postgres. This metric is reported in your project's [billing usage](https://app.supabase.com/project/_/settings/billing/usage) and is updated daily.
|
||||
Database size is the total size of used storage from your database, whereas disk size describes the size of the underlying available storage.
|
||||
|
||||
For an instantaneous live view of the DB size, you can execute in Postgres:
|
||||
|
||||
```sql
|
||||
select
|
||||
sum(pg_database_size (pg_database.datname)) / (1024 * 1024) as db_size_mb
|
||||
from
|
||||
pg_database;
|
||||
```
|
||||
|
||||
This value is also reported in the [database settings page](https://app.supabase.com/project/_/settings/database).
|
||||
|
||||
## Database storage management
|
||||
|
||||
Supabase uses network-attached storage to balance performance with scalability.
|
||||
For Pro and Enterprise projects, disk size expands ~1.5x automatically (e.g., 8GB -> 12GB) when you reach 90% of the disk size.
|
||||
Disk size expansion can only occur once every six hours.
|
||||
Pro projects can store up to 1024TB.
|
||||
|
||||
All projects enter read-only mode when you reach 95% of the disk size. In read-only mode, clients will encounter errors such as `cannot execute INSERT in a read-only transaction`.
|
||||
Regular operation (read-write mode) is automatically re-enabled once usage is below 95% of the disk size.
|
||||
|
||||
If you need more than 1024TB of disk size or require multiple storage expansions in a short period of time, [contact us](https://app.supabase.com/support/new) to learn more about the Enterprise plan.
|
||||
|
||||
### Increasing available disk size
|
||||
|
||||
1. [Upgrade to the Pro or Enterprise plan](https://app.supabase.com/project/_/settings/billing/subscription) to increase your quota and expand your disk size automatically.
|
||||
|
||||
2. Delete data from your project's database to lower its disk usage. If your database is already in read-only mode, run the following command to change the transaction mode to read-write for your session:
|
||||
|
||||
```sql
|
||||
SET
|
||||
default_transaction_read_only = 'off';
|
||||
```
|
||||
|
||||
This allows you to delete data from within the session.
|
||||
|
||||
### Preoccupied Space
|
||||
|
||||
When launching a new project, your database size will be roughly ~40-60mb.
|
||||
The space is used up by preinstalled extensions, schema/data used by our services that are offered with each project and default Postgres data.
|
||||
|
||||
When installing additional extensions, even if you don't actively use them, additional database size is used.
|
||||
|
||||
## Vacuum operations
|
||||
|
||||
Postgres does not immediately reclaim the physical space used by dead tuples (i.e., deleted rows) in the DB. Instead, they are internally marked as removed until a [vacuum operation](https://www.postgresql.org/docs/current/routine-vacuuming.html) is executed.
|
||||
As a result, deleting data from your database may not immediately reduce the reported disk usage.
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
Vacuum operations can temporarily increase resource utilization, which may adversely impact the observed performance of your project until the maintenance is completed.
|
||||
|
||||
</Admonition>
|
||||
|
||||
Supabase projects have automatic vacuuming enabled, which ensures that these operations are performed regularly to keep the database healthy and performant.
|
||||
However, it can be necessary to either [fine-tune](https://www.percona.com/blog/2018/08/10/tuning-autovacuum-in-postgresql-and-autovacuum-internals/)
|
||||
the [autovacuum parameters](https://www.enterprisedb.com/blog/postgresql-vacuum-and-analyze-best-practice-tips),
|
||||
or [manually initiate](https://www.postgresql.org/docs/current/sql-vacuum.html) vacuum operations.
|
||||
For example, running a manual vacuum after deleting large amounts of data from your DB could help reduce the reported disk usage by Postgres.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -168,7 +168,7 @@ Each log event stores metadata an array of objects with multiple levels, and can
|
||||
For example, to query the edge logs without any joins:
|
||||
|
||||
```sql
|
||||
select timestamp, metadata from edge_logs t
|
||||
select timestamp, metadata from edge_logs as t;
|
||||
```
|
||||
|
||||
The resulting `metadata` key is rendered as an array of objects in the Logs Explorer. In the following diagram, each box represents a nested array of objects:
|
||||
@@ -183,10 +183,11 @@ To query for a nested value, add a join for each array level:
|
||||
|
||||
```sql
|
||||
select timestamp, request.method, header.cf_ipcountry
|
||||
from edge_logs t
|
||||
cross join unnest(t.metadata) as metadata
|
||||
cross join unnest(metadata.request) as request
|
||||
cross join unnest(request.headers) as header
|
||||
from
|
||||
edge_logs as t
|
||||
cross join unnest(t.metadata) as metadata
|
||||
cross join unnest(metadata.request) as request
|
||||
cross join unnest(request.headers) as header;
|
||||
```
|
||||
|
||||
This surfaces the following columns available for selection:
|
||||
@@ -217,17 +218,19 @@ Instead, select only the values required.
|
||||
-- ❌ Avoid doing this
|
||||
select
|
||||
datetime(timestamp),
|
||||
m as metadata -- <- metadata contains many nested keys
|
||||
from edge_logs t
|
||||
cross join unnest(t.metadata) as m;
|
||||
m as metadata -- <- metadata contains many nested keys
|
||||
from
|
||||
edge_logs as t
|
||||
cross join unnest(t.metadata) as m;
|
||||
|
||||
-- ✅ Do this
|
||||
select
|
||||
datetime(timestamp),
|
||||
r.method -- <- select only the required values
|
||||
from edge_logs t
|
||||
cross join unnest(t.metadata) as m
|
||||
cross join unnest(m.request) as r
|
||||
datetime(timestamp),
|
||||
r.method -- <- select only the required values
|
||||
from
|
||||
edge_logs as t
|
||||
cross join unnest(t.metadata) as m
|
||||
cross join unnest(m.request) as r;
|
||||
```
|
||||
|
||||
### Examples and Templates
|
||||
@@ -238,11 +241,12 @@ For example, you can enter the following query in the SQL Editor to retrieve eac
|
||||
|
||||
```sql
|
||||
select datetime(timestamp), h.x_real_ip
|
||||
from edge_logs
|
||||
from
|
||||
edge_logs
|
||||
cross join unnest(metadata) as m
|
||||
cross join unnest(m.request) AS r
|
||||
cross join unnest(r.headers) AS h
|
||||
where h.x_real_ip is not null and r.method = "GET"
|
||||
cross join unnest(m.request) as r
|
||||
cross join unnest(r.headers) as h
|
||||
where h.x_real_ip is not null and r.method = "GET";
|
||||
```
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
@@ -8,6 +8,161 @@ export const meta = {
|
||||
|
||||
The Supabase platform automatically optimizes your Postgres database to take advantage of the compute resources of the tier your project is on. However, these optimizations are based on assumptions about the type of workflow the project is being utilized for, and it is likely that better results can be obtained by tuning the database for your particular workflow.
|
||||
|
||||
## Examining Query Performance
|
||||
|
||||
Unoptimized queries are a major cause of poor database performance. The techniques on this page can help you identify and understand queries that take the most time and resources from your database.
|
||||
|
||||
Database performance is a large topic and many factors can contribute. Some of the most common causes of poor performance include:
|
||||
|
||||
- An inefficiently designed schema
|
||||
- Inefficiently designed queries
|
||||
- A lack of indexes causing slower than required queries over large tables
|
||||
- Unused indexes causing slow `INSERT`, `UPDATE` and `DELETE` operations
|
||||
- Not enough compute resources, such as memory, causing your database to go to disk for results too often
|
||||
- Lock contention from multiple queries operating on highly utilized tables
|
||||
- Large amount of bloat on your tables causing poor query planning
|
||||
|
||||
Thankfully there are solutions to all these issues, which we will cover in the following sections.
|
||||
|
||||
### Postgres Cumulative Statistics system
|
||||
|
||||
Postgres collects data about its own operations using the [cumulative statistics system](https://www.postgresql.org/docs/current/monitoring-stats.html). In addition to this, every Supabase project has the [pg_stat_statements extension](/docs/guides/database/extensions/pg_stat_statements) enabled by default. This extension records query execution performance details and is the best way to find inefficient queries. This information can be combined with the Postgres query plan analyzer to develop more efficient queries.
|
||||
|
||||
Here are some example queries to get you started.
|
||||
|
||||
#### Most frequently called queries:
|
||||
|
||||
```sql
|
||||
select
|
||||
auth.rolname,
|
||||
statements.query,
|
||||
statements.calls,
|
||||
-- -- Postgres 13, 14, 15
|
||||
statements.total_exec_time + statements.total_plan_time as total_time,
|
||||
statements.min_exec_time + statements.min_plan_time as min_time,
|
||||
statements.max_exec_time + statements.max_plan_time as max_time,
|
||||
statements.mean_exec_time + statements.mean_plan_time as mean_time,
|
||||
-- -- Postgres <= 12
|
||||
-- total_time,
|
||||
-- min_time,
|
||||
-- max_time,
|
||||
-- mean_time,
|
||||
statements.rows / statements.calls as avg_rows
|
||||
from
|
||||
pg_stat_statements as statements
|
||||
inner join pg_authid as auth on statements.userid = auth.oid
|
||||
order by statements.calls desc
|
||||
limit 100;
|
||||
```
|
||||
|
||||
This query shows:
|
||||
|
||||
- query statistics, ordered by the number of times each query has been executed
|
||||
- the role that ran the query
|
||||
- the number of times it has been called
|
||||
- the average number of rows returned
|
||||
- the cumulative total time the query has spent running
|
||||
- the min, max and mean query times.
|
||||
|
||||
This provides useful information about the queries you run most frequently. Queries that have high `max_time` or `mean_time` times and are being called often can be good candidates for optimization.
|
||||
|
||||
#### Slowest queries by execution time:
|
||||
|
||||
```sql
|
||||
select
|
||||
auth.rolname,
|
||||
statements.query,
|
||||
statements.calls,
|
||||
-- -- Postgres 13, 14, 15
|
||||
statements.total_exec_time + statements.total_plan_time as total_time,
|
||||
statements.min_exec_time + statements.min_plan_time as min_time,
|
||||
statements.max_exec_time + statements.max_plan_time as max_time,
|
||||
statements.mean_exec_time + statements.mean_plan_time as mean_time,
|
||||
-- -- Postgres <= 12
|
||||
-- total_time,
|
||||
-- min_time,
|
||||
-- max_time,
|
||||
-- mean_time,
|
||||
statements.rows / statements.calls as avg_rows
|
||||
from
|
||||
pg_stat_statements as statements
|
||||
inner join pg_authid as auth on statements.userid = auth.oid
|
||||
order by max_time desc
|
||||
limit 100;
|
||||
```
|
||||
|
||||
This query will show you statistics about queries ordered by the maximum execution time. It is similar to the query above ordered by calls, but this one highlights outliers that may have high executions times. Queries which have high or mean execution times are good candidates for optimisation.
|
||||
|
||||
#### Most time consuming queries:
|
||||
|
||||
```sql
|
||||
select
|
||||
auth.rolname,
|
||||
statements.query,
|
||||
statements.calls,
|
||||
statements.total_exec_time + statements.total_plan_time as total_time,
|
||||
to_char(
|
||||
(
|
||||
(statements.total_exec_time + statements.total_plan_time) / sum(
|
||||
statements.total_exec_time + statements.total_plan_time
|
||||
) over ()
|
||||
) * 100,
|
||||
'FM90D0'
|
||||
) || '%' as prop_total_time
|
||||
from
|
||||
pg_stat_statements as statements
|
||||
inner join pg_authid as auth on statements.userid = auth.oid
|
||||
order by total_time desc
|
||||
limit 100;
|
||||
```
|
||||
|
||||
This query will show you statistics about queries ordered by the cumulative total execution time. It shows the total time the query has spent running as well as the proportion of total execution time the query has taken up.
|
||||
|
||||
Queries which are the most time consuming are not necessarily bad, you may have a very effiecient and frequently ran queries that end up taking a large total % time, but it can be useful to help spot queries that are taking up more time than they should.
|
||||
|
||||
### Hit rate
|
||||
|
||||
Generally for most applications a small percentage of data is accessed more regularly than the rest. To make sure that your regularly accessed data is available, Postgres tracks your data access patterns and keeps this in its [shared_buffers](https://www.postgresql.org/docs/15/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-MEMORY) cache.
|
||||
|
||||
Applications with lower cache hit rates generally perform more poorly since they have to hit the disk to get results rather than serving them from memory. Very poor hit rates can also cause you to burst past your [Disk I/O limits](https://supabase.com/docs/guides/platform/compute-add-ons#disk-io-bandwidth) causing significant performance issues.
|
||||
|
||||
You can view your cache and index hit rate by executing the following query:
|
||||
|
||||
```sql
|
||||
select
|
||||
'index hit rate' as name,
|
||||
(sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read), 0) * 100 as ratio
|
||||
from pg_statio_user_indexes
|
||||
union all
|
||||
select
|
||||
'table hit rate' as name,
|
||||
sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read), 0) * 100 as ratio
|
||||
from pg_statio_user_tables;
|
||||
```
|
||||
|
||||
This shows the ratio of data blocks fetched from the Postgres [shared_buffers](https://www.postgresql.org/docs/15/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-MEMORY) cache against the data blocks that were read from disk/OS cache.
|
||||
|
||||
If either of your index or table hit rate are < 99% then this can indicate your compute plan is too small for your current workload and you would benefit from more memory. [Upgrading your compute](https://supabase.com/docs/guides/platform/compute-add-ons) is easy and can be done from your [project dashboard](https://app.supabase.com/project/_/settings/billing/subscription).
|
||||
|
||||
### Optimizing poor performing queries
|
||||
|
||||
Postgres has built in tooling to help you optimize poorly performing queries. You can use the [query plan analyzer](https://www.postgresql.org/docs/current/sql-explain.html) on any expensive queries that you have identified:
|
||||
|
||||
```sql
|
||||
explain analyze <query-statement-here>;
|
||||
```
|
||||
|
||||
Be careful using `explain analyze` with `insert`/`update`/`delete` queries, because the query will actually run, and could have unintended side-effects.
|
||||
|
||||
Using the query plan analyzer to optimize your queries is a large topic, with a number of online resources available:
|
||||
|
||||
- [Official docs.](https://www.postgresql.org/docs/current/using-explain.html)
|
||||
- [The Art of PostgreSQL.](https://theartofpostgresql.com/explain-plan-visualizer/)
|
||||
- [Postgres Wiki.](https://wiki.postgresql.org/wiki/Using_EXPLAIN)
|
||||
- [Enterprise DB.](https://www.enterprisedb.com/blog/postgresql-query-optimization-performance-tuning-with-explain-analyze)
|
||||
|
||||
You can pair the information available from `pg_stat_statements` with the detailed system metrics available [via your metrics endpoint](../platform/metrics) to better understand the behavior of your DB and the queries you're executing against it.
|
||||
|
||||
## Optimizing the number of connections
|
||||
|
||||
By default, the number of connections allowed to Postgres and PgBouncer is configured based on the resources available to the database.
|
||||
@@ -64,33 +219,6 @@ alter system reset <other-overridden-conf>;
|
||||
|
||||
Configuring the number of PgBouncer connections is not supported at this time.
|
||||
|
||||
## Examining Query Performance
|
||||
|
||||
Every Supabase project has [the pg_stat_statements extension](https://www.postgresql.org/docs/14/pgstatstatements.html) enabled by default. This extension records query execution performance details and is the best way to find queries that take the most time to execute. This information can be combined with the Postgres query plan analyzer to develop more efficient queries.
|
||||
|
||||
Obtaining information from pg_stat_statements:
|
||||
|
||||
```sql
|
||||
select mean_exec_time + stddev_exec_time, * from pg_stat_statements order by 1 desc;
|
||||
```
|
||||
|
||||
Using the query plan analyzer on your expensive queries:
|
||||
|
||||
```sql
|
||||
explain analyze <query-statement-here>;
|
||||
```
|
||||
|
||||
Be careful using `explain analyze` with `insert`/`update`/`delete` queries, because the query will actually run, and could have unintended side-effects.
|
||||
|
||||
Using the query plan analyzer to optimize your queries is a large topic, with a number of online resources available:
|
||||
|
||||
- [Official docs.](https://www.postgresql.org/docs/current/using-explain.html)
|
||||
- [The Art of PostgreSQL.](https://theartofpostgresql.com/explain-plan-visualizer/)
|
||||
- [Postgres Wiki.](https://wiki.postgresql.org/wiki/Using_EXPLAIN)
|
||||
- [Enterprise DB.](https://www.enterprisedb.com/blog/postgresql-query-optimization-performance-tuning-with-explain-analyze)
|
||||
|
||||
You can pair the information available from `pg_stat_statements` with the detailed system metrics available [via your metrics endpoint](../platform/metrics) to better understand the behavior of your DB and the queries you're executing against it.
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -113,6 +113,7 @@ Build a basic Todo List with Supabase and your favorite frontend framework:
|
||||
- In-depth self-hosting guide using Nginx [Blog](https://dev.to/chronsyn/self-hosting-with-supabase-1aii)
|
||||
- Build an Email and Social Auth for Next JS with Supabase, Tailwind CSS 3.0 and TypeScript [Blog](https://creativedesignsguru.com/next-js-supabase-auth/)
|
||||
- Link Shortener using Supabase and Ory [3-part Blog Series](https://www.ory.sh/tutorial-url-shortener-supabase-ory-integration-backend/)
|
||||
- Building a CRUD API with FastAPI and Supabase: A Step-by-Step Guide [Blog](https://blog.theinfosecguy.xyz/building-a-crud-api-with-fastapi-and-supabase-a-step-by-step-guide)
|
||||
|
||||
### Example apps
|
||||
|
||||
|
||||
@@ -41,8 +41,10 @@ client libraries. Here we create a bucket called "avatars":
|
||||
```sql
|
||||
-- Use Postgres to create a bucket.
|
||||
|
||||
insert into storage.buckets (id, name)
|
||||
values ('avatars', 'avatars');
|
||||
insert into storage.buckets
|
||||
(id, name)
|
||||
values
|
||||
('avatars', 'avatars');
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
@@ -158,8 +158,8 @@ Supabase comes with two built-in helper functions: `auth.uid()` and `auth.jwt()`
|
||||
To create your own functions, navigate to the SQL editor and create a a new query.
|
||||
|
||||
```sql
|
||||
create or replace function user_agent()
|
||||
returns text
|
||||
create or replace function user_agent()
|
||||
returns text
|
||||
language sql
|
||||
as $$
|
||||
select nullif(current_setting('request.headers', true)::json->>'user-agent', '')::text;
|
||||
|
||||
@@ -45,12 +45,16 @@ Let's say we create a leaderboard table. We want people on our website to be abl
|
||||
|
||||
```sql
|
||||
create table leaderboard (
|
||||
name text,
|
||||
score int
|
||||
name text,
|
||||
score int
|
||||
);
|
||||
|
||||
insert into leaderboard(name, score)
|
||||
values ('Paul', 100), ('Leto', 50), ('Chani', 200);
|
||||
insert into leaderboard
|
||||
(name, score)
|
||||
values
|
||||
('Paul', 100),
|
||||
('Leto', 50),
|
||||
('Chani', 200);
|
||||
```
|
||||
|
||||
Now let's set up a client to read the data, I've created a repl here to show a living example: [https://replit.com/@awalias/supabase-leaderboard-demo#index.js](https://replit.com/@awalias/supabase-leaderboard-demo#index.js). If you copy the snippet you can plug in your own Supabase URL and anon key.
|
||||
|
||||
@@ -17,6 +17,7 @@ Dave Wilson
|
||||
Div Arora
|
||||
Egor Romanov
|
||||
Fabrizio Fenoglio
|
||||
Francesco Sansalvadore
|
||||
Haydn Maley
|
||||
Hieu Pham
|
||||
Inian P
|
||||
|
||||
|
Before Width: | Height: | Size: 514 KiB After Width: | Height: | Size: 1.0 MiB |
@@ -18,12 +18,6 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/api</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/cli</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -36,12 +30,6 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/examples</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -90,12 +78,6 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/index</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/handbook/contributing</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -108,30 +90,12 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/new/auth</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/index</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/tutorials/nextjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/api/generating-types</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-captcha</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -216,6 +180,12 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/api</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/arrays</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -318,60 +288,6 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/auth</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/cicd-workflow</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/cors</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/debugging</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/import-maps</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/local-development</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/quickstart</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/schedule-functions</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/secrets</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/integrations/appsmith</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -534,6 +450,60 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/auth</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/cicd-workflow</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/cors</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/debugging</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/import-maps</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/local-development</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/quickstart</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/schedule-functions</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/secrets</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/access-control</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -559,7 +529,7 @@
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/database-usage</loc>
|
||||
<loc>https://supabase.com/docs/guides/platform/database-size</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
@@ -612,6 +582,12 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/ssl-enforcement</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/sso</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -624,12 +600,24 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/realtime/broadcast</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/realtime/postgres-changes</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/realtime/presence</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/realtime/quickstart</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -642,6 +630,12 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/examples</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/glossary</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -715,49 +709,139 @@
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/http</loc>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/auth-ui</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgcron</loc>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/nextjs-server-components</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgnet</loc>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/nextjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgtap</loc>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/remix</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/plv8</loc>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/sveltekit</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/postgis</loc>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-apple</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/rum</loc>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-azure</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/uuid-ossp</loc>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-bitbucket</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-discord</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-facebook</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-github</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-gitlab</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-google</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-keycloak</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-linkedin</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-notion</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-slack</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-spotify</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-twitch</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-twitter</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-workos</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-zoom</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/api/generating-types</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
@@ -888,36 +972,6 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/auth-ui</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/nextjs-server-components</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/nextjs</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/remix</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/auth-helpers/sveltekit</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/phone-login/messagebird</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -948,6 +1002,12 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/examples/discord-bot</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/examples/github-actions</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -960,6 +1020,12 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/examples/openai</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/examples/storage-caching</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -978,6 +1044,186 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/functions/examples/upstash-redis</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/sso/azure</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/sso/gsuite</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/sso/okta</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/http</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/hypopg</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pg-safeupdate</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pg_graphql</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pg_hashids</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pg_jsonschema</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pg_plan_filter</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pg_stat_monitor</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pg_stat_statements</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgaudit</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgcron</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgjwt</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgnet</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgrepack</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgroonga</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgrouting</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgsodium</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgtap</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/pgvector</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/plpgsql_check</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/plv8</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/postgis</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/rum</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/timescaledb</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/uuid-ossp</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/database/extensions/wrappers</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/firebase-auth</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -1003,25 +1249,7 @@
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/self-hosting/realtime/config</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/sso/azure</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/sso/gsuite</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/platform/sso/okta</loc>
|
||||
<loc>https://supabase.com/docs/guides/resources/migrating-to-supabase/render</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
@@ -1032,114 +1260,36 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/postgres/dropping-all-tables-in-schema</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/postgres/first-row-in-group</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/resources/postgres/which-version-of-postgres</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/self-hosting/realtime/config</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/self-hosting/storage/config</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-apple</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-azure</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-bitbucket</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-discord</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-facebook</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-github</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-gitlab</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-google</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-keycloak</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-linkedin</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-notion</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-slack</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-spotify</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-twitch</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-twitter</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-workos</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/social-login/auth-zoom</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/initializing</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -1562,6 +1712,12 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/db-returns</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/reference/javascript/functions-invoke</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
@@ -3867,4 +4023,4 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
</urlset>
|
||||
</urlset>
|
||||
@@ -11,7 +11,7 @@ date: '2023-02-07'
|
||||
toc_depth: 3
|
||||
---
|
||||
|
||||
We all know that Microsoft's real agenda for pouring billions into OpenAI to revive their favorite friend Clippy.
|
||||
We all know that Microsoft's real agenda for pouring billions into OpenAI is to revive their favorite friend Clippy.
|
||||
Today, we're doing our part to support the momentum by releasing “Supabase Clippy” for our docs (and we don't expect this name to last long before the lawyers catch on).
|
||||
|
||||

|
||||
@@ -30,7 +30,7 @@ Our product suite has grown in the past 2 years and our docs have grown as a res
|
||||
|
||||
### The “ask” interface
|
||||
|
||||
Developers have recently gained an the ability to trust a bot. Where Clippy failed, ChatGPT succeeded.
|
||||
Developers have recently gained the ability to trust a bot. Where Clippy failed, ChatGPT succeeded.
|
||||
|
||||
This is convenient timing for us, since our documentation content is more than the average developer wants to consume in one go. Today we're providing a similar interface to ChatGPT which is trained on our own docs.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ const data: CommunityItem[] = [
|
||||
},
|
||||
{
|
||||
title: 'GitHub',
|
||||
stat: '44,000+',
|
||||
stat: '46,000+',
|
||||
statLabel: 'GitHub stars',
|
||||
img: 'github.png',
|
||||
invertImgDarkMode: true,
|
||||
@@ -42,7 +42,7 @@ const data: CommunityItem[] = [
|
||||
},
|
||||
{
|
||||
title: 'Twitter',
|
||||
stat: '54,000+',
|
||||
stat: '60,000+',
|
||||
statLabel: 'Followers',
|
||||
img: 'twitter.png',
|
||||
detail: () => (
|
||||
|
||||
@@ -1857,4 +1857,24 @@ module.exports = [
|
||||
source: '/docs/reference/javascript/v0/rpc',
|
||||
destination: '/docs/reference/javascript/rpc',
|
||||
},
|
||||
{
|
||||
permanent: true,
|
||||
source: '/docs/guides/platform/database-usage',
|
||||
destination: '/docs/guides/platform/database-size',
|
||||
},
|
||||
{
|
||||
permanent: true,
|
||||
source: '/docs/guides/resources/postgres/dropping-all-tables-in-schema',
|
||||
destination: '/docs/guides/database/postgres/dropping-all-tables-in-schema',
|
||||
},
|
||||
{
|
||||
permanent: true,
|
||||
source: '/docs/guides/resources/postgres/first-row-in-group',
|
||||
destination: '/docs/guides/database/postgres/first-row-in-group',
|
||||
},
|
||||
{
|
||||
permanent: true,
|
||||
source: '/docs/guides/resources/postgres/which-version-of-postgres',
|
||||
destination: '/docs/guides/database/postgres/which-version-of-postgres',
|
||||
},
|
||||
]
|
||||
@@ -78,7 +78,6 @@
|
||||
"file-loader": "^6.2.0",
|
||||
"postcss": "^8.4.18",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"prettier": "^2.2.1",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"tsconfig": "*"
|
||||
},
|
||||
|
||||
@@ -87,8 +87,8 @@ An Information request about Supabase or feature request.
|
||||
|
||||
| Severity Level | Standard | Priority | Priority Plus |
|
||||
| -------------- | ------------------------------------- | ------------------------------------- | -------------------------------------- |
|
||||
| 1. Urgent | 1 business hour<br />24/7 × 365 | 1 business hour<br />24/7 × 365 | 1 business hour<br />24/7 × 365 |
|
||||
| 2. High | 4 business hours<br />Monday - Friday | 2 business hours<br />Monday - Friday | 2 business hours<br />24/7 × 365 |
|
||||
| 1. Urgent | 1 hour<br />24/7 × 365 | 1 hour<br />24/7 × 365 | 1 hour<br />24/7 × 365 |
|
||||
| 2. High | 4 business hours<br />Monday - Friday | 2 business hours<br />Monday - Friday | 2 hours<br />24/7 × 365 |
|
||||
| 3. Normal | 1 business day<br />Monday - Friday | 1 business day<br />Monday - Friday | 12 business hours<br />Monday - Friday |
|
||||
| 4. Low | 2 business days<br />Monday - Friday | 2 business days<br />Monday - Friday | 1 business day<br />Monday - Friday |
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 760 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 349 KiB |
|
After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 703 KiB |
|
After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 485 KiB |
|
After Width: | Height: | Size: 185 KiB |
@@ -0,0 +1,3 @@
|
||||
# Update these with your Supabase details from your project settings > API
|
||||
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
@@ -0,0 +1,75 @@
|
||||
# Todo example using Supabase
|
||||
|
||||
- Frontend:
|
||||
- [Next.js](https://github.com/vercel/next.js) - a React framework for production.
|
||||
- [Tailwind](https://tailwindcss.com/) for styling and layout.
|
||||
- [Supabase.js](https://supabase.com/docs/library/getting-started) for user management and realtime data syncing.
|
||||
- Backend:
|
||||
- [app.supabase.com](https://app.supabase.com/): hosted Postgres database with restful API for usage with Supabase.js.
|
||||
|
||||
## Deploy with Vercel
|
||||
|
||||
The Vercel deployment will guide you through creating a Supabase account and project. After installation of the Supabase integration, all relevant environment variables will be set up so that the project is usable immediately after deployment 🚀
|
||||
|
||||
[](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fsupabase%2Fexamples%2Ftree%2Fmain%2Fsupabase-js-v1%2Ftodo-list%2Fnextjs-todo-list&project-name=supabase-todo-list&repository-name=supabase-todo-list&demo-title=Todo%20list&demo-description=An%20example%20web%20app%20using%20Supabase%20and%20Next.js&demo-url=https%3A%2F%2Fsupabase-nextjs-todo-list.vercel.app&demo-image=https%3A%2F%2Fi.imgur.com%2FGJauPlN.png&integration-ids=oac_jUduyjQgOyzev1fjrW83NYOv&external-id=supabase-todo-list)
|
||||
|
||||
## Build from scratch
|
||||
|
||||
### 1. Create new project
|
||||
|
||||
Sign up to Supabase - [https://app.supabase.com](https://app.supabase.com) and create a new project. Wait for your database to start.
|
||||
|
||||
### 2. Run "Todo List" Quickstart
|
||||
|
||||
Once your database has started, run the "Todo List" quickstart. Inside of your project, enter the `SQL editor` tab and scroll down until you see `TODO LIST: Build a basic todo list with Row Level Security`.
|
||||
|
||||
### 3. Get the URL and Key
|
||||
|
||||
Go to the Project Settings (the cog icon), open the API tab, and find your API URL and `anon` key, you'll need these in the next step.
|
||||
|
||||
The `anon` key is your client-side API key. It allows "anonymous access" to your database, until the user has logged in. Once they have logged in, the keys will switch to the user's own login token. This enables row level security for your data. Read more about this [below](#postgres-row-level-security).
|
||||
|
||||

|
||||
|
||||
**_NOTE_**: The `service_role` key has full access to your data, bypassing any security policies. These keys have to be kept secret and are meant to be used in server environments and never on a client or browser.
|
||||
|
||||
## Supabase details
|
||||
|
||||
### Postgres Row level security
|
||||
|
||||
This project uses very high-level Authorization using Postgres' Role Level Security.
|
||||
When you start a Postgres database on Supabase, we populate it with an `auth` schema, and some helper functions.
|
||||
When a user logs in, they are issued a JWT with the role `authenticated` and their UUID.
|
||||
We can use these details to provide fine-grained control over what each user can and cannot do.
|
||||
|
||||
This is a trimmed-down schema, with the policies:
|
||||
|
||||
```sql
|
||||
create table todos (
|
||||
id bigint generated by default as identity primary key,
|
||||
user_id uuid references auth.users not null,
|
||||
task text check (char_length(task) > 3),
|
||||
is_complete boolean default false,
|
||||
inserted_at timestamp with time zone default timezone('utc'::text, now()) not null
|
||||
);
|
||||
|
||||
alter table todos enable row level security;
|
||||
|
||||
create policy "Individuals can create todos." on todos for
|
||||
insert with check (auth.uid() = user_id);
|
||||
|
||||
create policy "Individuals can view their own todos. " on todos for
|
||||
select using (auth.uid() = user_id);
|
||||
|
||||
create policy "Individuals can update their own todos." on todos for
|
||||
update using (auth.uid() = user_id);
|
||||
|
||||
create policy "Individuals can delete their own todos." on todos for
|
||||
delete using (auth.uid() = user_id);
|
||||
```
|
||||
|
||||
## Authors
|
||||
|
||||
- [Supabase](https://supabase.com)
|
||||
|
||||
Supabase is open source. We'd love for you to follow along and get involved at https://github.com/supabase/supabase
|
||||
@@ -0,0 +1,151 @@
|
||||
import { Database } from '@/lib/schema'
|
||||
import { Session, useSupabaseClient } from '@supabase/auth-helpers-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
type Todos = Database['public']['Tables']['todos']['Row']
|
||||
|
||||
export default function TodoList({ session }: { session: Session }) {
|
||||
const supabase = useSupabaseClient<Database>()
|
||||
const [todos, setTodos] = useState<Todos[]>([])
|
||||
const [newTaskText, setNewTaskText] = useState('')
|
||||
const [errorText, setErrorText] = useState('')
|
||||
|
||||
const user = session.user
|
||||
|
||||
useEffect(() => {
|
||||
const fetchTodos = async () => {
|
||||
const { data: todos, error } = await supabase
|
||||
.from('todos')
|
||||
.select('*')
|
||||
.order('id', { ascending: true })
|
||||
|
||||
if (error) console.log('error', error)
|
||||
else setTodos(todos)
|
||||
}
|
||||
|
||||
fetchTodos()
|
||||
}, [supabase])
|
||||
|
||||
const addTodo = async (taskText: string) => {
|
||||
let task = taskText.trim()
|
||||
if (task.length) {
|
||||
const { data: todo, error } = await supabase
|
||||
.from('todos')
|
||||
.insert({ task, user_id: user.id })
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
setErrorText(error.message)
|
||||
} else {
|
||||
setTodos([...todos, todo])
|
||||
setNewTaskText('')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deleteTodo = async (id: number) => {
|
||||
try {
|
||||
await supabase.from('todos').delete().eq('id', id).throwOnError()
|
||||
setTodos(todos.filter((x) => x.id != id))
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<h1 className="mb-12">Todo List.</h1>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
addTodo(newTaskText)
|
||||
}}
|
||||
className="flex gap-2 my-2"
|
||||
>
|
||||
<input
|
||||
className="rounded w-full p-2"
|
||||
type="text"
|
||||
placeholder="make coffee"
|
||||
value={newTaskText}
|
||||
onChange={(e) => {
|
||||
setErrorText('')
|
||||
setNewTaskText(e.target.value)
|
||||
}}
|
||||
/>
|
||||
<button className="btn-black" type="submit">
|
||||
Add
|
||||
</button>
|
||||
</form>
|
||||
{!!errorText && <Alert text={errorText} />}
|
||||
<div className="bg-white shadow overflow-hidden rounded-md">
|
||||
<ul>
|
||||
{todos.map((todo) => (
|
||||
<Todo key={todo.id} todo={todo} onDelete={() => deleteTodo(todo.id)} />
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Todo = ({ todo, onDelete }: { todo: Todos; onDelete: () => void }) => {
|
||||
const supabase = useSupabaseClient<Database>()
|
||||
const [isCompleted, setIsCompleted] = useState(todo.is_complete)
|
||||
|
||||
const toggle = async () => {
|
||||
try {
|
||||
const { data } = await supabase
|
||||
.from('todos')
|
||||
.update({ is_complete: !isCompleted })
|
||||
.eq('id', todo.id)
|
||||
.throwOnError()
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (data) setIsCompleted(data.is_complete)
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<li className="w-full block cursor-pointer hover:bg-gray-200 focus:outline-none focus:bg-gray-200 transition duration-150 ease-in-out">
|
||||
<div className="flex items-center px-4 py-4 sm:px-6">
|
||||
<div className="min-w-0 flex-1 flex items-center">
|
||||
<div className="text-sm leading-5 font-medium truncate">{todo.task}</div>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
className="cursor-pointer"
|
||||
onChange={(e) => toggle()}
|
||||
type="checkbox"
|
||||
checked={isCompleted ? true : false}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
onDelete()
|
||||
}}
|
||||
className="w-4 h-4 ml-2 border-2 hover:border-black rounded"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="gray">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
const Alert = ({ text }: { text: string }) => (
|
||||
<div className="rounded-md bg-red-100 p-4 my-3">
|
||||
<div className="text-sm leading-5 text-red-700">{text}</div>
|
||||
</div>
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
import { Database } from './schema'
|
||||
|
||||
export const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
|
||||
)
|
||||
@@ -0,0 +1,49 @@
|
||||
export type Json =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| { [key: string]: Json }
|
||||
| Json[]
|
||||
|
||||
export interface Database {
|
||||
public: {
|
||||
Tables: {
|
||||
todos: {
|
||||
Row: {
|
||||
id: number
|
||||
inserted_at: string
|
||||
is_complete: boolean | null
|
||||
task: string | null
|
||||
user_id: string
|
||||
}
|
||||
Insert: {
|
||||
id?: number
|
||||
inserted_at?: string
|
||||
is_complete?: boolean | null
|
||||
task?: string | null
|
||||
user_id: string
|
||||
}
|
||||
Update: {
|
||||
id?: number
|
||||
inserted_at?: string
|
||||
is_complete?: boolean | null
|
||||
task?: string | null
|
||||
user_id?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
Views: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Functions: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Enums: {
|
||||
[_ in never]: never
|
||||
}
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "nextjs-todo-list",
|
||||
"version": "2.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "concurrently \"npm run dev:css\" \"next dev\"",
|
||||
"dev:css": "tailwindcss -w -i ./styles/tailwind.css -o styles/app.css",
|
||||
"build": "next build",
|
||||
"build:css": "tailwindcss -m -i ./styles/tailwind.css -o styles/app.css",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/font": "13.1.6",
|
||||
"@supabase/auth-helpers-react": "^0.3.1",
|
||||
"@supabase/auth-ui-react": "^0.2.8",
|
||||
"@supabase/supabase-js": "^2.8.0",
|
||||
"@types/node": "18.14.0",
|
||||
"@types/react": "18.0.28",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"eslint": "8.34.0",
|
||||
"eslint-config-next": "13.1.6",
|
||||
"next": "13.1.6",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^7.6.0",
|
||||
"tailwindcss": "^3.2.7"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { supabase } from '@/lib/initSupabase'
|
||||
import '@/styles/app.css'
|
||||
import { SessionContextProvider } from '@supabase/auth-helpers-react'
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<SessionContextProvider supabaseClient={supabase}>
|
||||
<Component {...pageProps} />
|
||||
</SessionContextProvider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Html, Head, Main, NextScript } from 'next/document'
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
type Data = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import Head from 'next/head'
|
||||
import { useSession, useSupabaseClient } from '@supabase/auth-helpers-react'
|
||||
import { Auth, ThemeSupa } from '@supabase/auth-ui-react'
|
||||
import TodoList from '@/components/TodoList'
|
||||
|
||||
export default function Home() {
|
||||
const session = useSession()
|
||||
const supabase = useSupabaseClient()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<meta name="description" content="Generated by create next app" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<div className="w-full h-full bg-gray-200">
|
||||
{!session ? (
|
||||
<div className="min-w-full min-h-screen flex items-center justify-center">
|
||||
<div className="w-full h-full flex justify-center items-center p-4">
|
||||
<div className="w-full h-full sm:h-auto sm:w-2/5 max-w-sm p-5 bg-white shadow flex flex-col text-base">
|
||||
<span className="font-sans text-4xl text-center pb-2 mb-1 border-b mx-4 align-center">
|
||||
Login
|
||||
</span>
|
||||
<Auth supabaseClient={supabase} appearance={{ theme: ThemeSupa }} theme="dark" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="w-full h-full flex flex-col justify-center items-center p-4"
|
||||
style={{ minWidth: 250, maxWidth: 600, margin: 'auto' }}
|
||||
>
|
||||
<TodoList session={session} />
|
||||
<button
|
||||
className="btn-black w-full mt-12"
|
||||
onClick={async () => {
|
||||
const { error } = await supabase.auth.signOut()
|
||||
if (error) console.log('Error logging out:', error.message)
|
||||
}}
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="31" fill="none"><g opacity=".9"><path fill="url(#a)" d="M13 .4v29.3H7V6.3h-.2L0 10.5V5L7.2.4H13Z"/><path fill="url(#b)" d="M28.8 30.1c-2.2 0-4-.3-5.7-1-1.7-.8-3-1.8-4-3.1a7.7 7.7 0 0 1-1.4-4.6h6.2c0 .8.3 1.4.7 2 .4.5 1 .9 1.7 1.2.7.3 1.6.4 2.5.4 1 0 1.7-.2 2.5-.5.7-.3 1.3-.8 1.7-1.4.4-.6.6-1.2.6-2s-.2-1.5-.7-2.1c-.4-.6-1-1-1.8-1.4-.8-.4-1.8-.5-2.9-.5h-2.7v-4.6h2.7a6 6 0 0 0 2.5-.5 4 4 0 0 0 1.7-1.3c.4-.6.6-1.3.6-2a3.5 3.5 0 0 0-2-3.3 5.6 5.6 0 0 0-4.5 0 4 4 0 0 0-1.7 1.2c-.4.6-.6 1.2-.6 2h-6c0-1.7.6-3.2 1.5-4.5 1-1.3 2.2-2.3 3.8-3C25 .4 26.8 0 28.8 0s3.8.4 5.3 1.1c1.5.7 2.7 1.7 3.6 3a7.2 7.2 0 0 1 1.2 4.2c0 1.6-.5 3-1.5 4a7 7 0 0 1-4 2.2v.2c2.2.3 3.8 1 5 2.2a6.4 6.4 0 0 1 1.6 4.6c0 1.7-.5 3.1-1.4 4.4a9.7 9.7 0 0 1-4 3.1c-1.7.8-3.7 1.1-5.8 1.1Z"/></g><defs><linearGradient id="a" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient><linearGradient id="b" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
||||
|
After Width: | Height: | Size: 629 B |
@@ -0,0 +1,844 @@
|
||||
/*
|
||||
! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com
|
||||
*/
|
||||
|
||||
/*
|
||||
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
|
||||
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
|
||||
*/
|
||||
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
/* 1 */
|
||||
border-width: 0;
|
||||
/* 2 */
|
||||
border-style: solid;
|
||||
/* 2 */
|
||||
border-color: #e5e7eb;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
::before,
|
||||
::after {
|
||||
--tw-content: '';
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use a consistent sensible line-height in all browsers.
|
||||
2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
3. Use a more readable tab size.
|
||||
4. Use the user's configured `sans` font-family by default.
|
||||
5. Use the user's configured `sans` font-feature-settings by default.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.5;
|
||||
/* 1 */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
/* 2 */
|
||||
-moz-tab-size: 4;
|
||||
/* 3 */
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
/* 3 */
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
/* 4 */
|
||||
font-feature-settings: normal;
|
||||
/* 5 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove the margin in all browsers.
|
||||
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
/* 1 */
|
||||
line-height: inherit;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Add the correct height in Firefox.
|
||||
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
|
||||
3. Ensure horizontal rules are visible by default.
|
||||
*/
|
||||
|
||||
hr {
|
||||
height: 0;
|
||||
/* 1 */
|
||||
color: inherit;
|
||||
/* 2 */
|
||||
border-top-width: 1px;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct text decoration in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
abbr:where([title]) {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the default font size and weight for headings.
|
||||
*/
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Reset links to optimize for opt-in styling instead of opt-out.
|
||||
*/
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font weight in Edge and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use the user's configured `mono` font family by default.
|
||||
2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp,
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
/* 1 */
|
||||
font-size: 1em;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
|
||||
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
|
||||
3. Remove gaps between table borders by default.
|
||||
*/
|
||||
|
||||
table {
|
||||
text-indent: 0;
|
||||
/* 1 */
|
||||
border-color: inherit;
|
||||
/* 2 */
|
||||
border-collapse: collapse;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Change the font styles in all browsers.
|
||||
2. Remove the margin in Firefox and Safari.
|
||||
3. Remove default padding in all browsers.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
/* 1 */
|
||||
font-size: 100%;
|
||||
/* 1 */
|
||||
font-weight: inherit;
|
||||
/* 1 */
|
||||
line-height: inherit;
|
||||
/* 1 */
|
||||
color: inherit;
|
||||
/* 1 */
|
||||
margin: 0;
|
||||
/* 2 */
|
||||
padding: 0;
|
||||
/* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inheritance of text transform in Edge and Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Remove default button styles.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type='button'],
|
||||
[type='reset'],
|
||||
[type='submit'] {
|
||||
-webkit-appearance: button;
|
||||
/* 1 */
|
||||
background-color: transparent;
|
||||
/* 2 */
|
||||
background-image: none;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Use the modern Firefox focus style for all focusable elements.
|
||||
*/
|
||||
|
||||
:-moz-focusring {
|
||||
outline: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
|
||||
*/
|
||||
|
||||
:-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct vertical alignment in Chrome and Firefox.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/*
|
||||
Correct the cursor style of increment and decrement buttons in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-inner-spin-button,
|
||||
::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the odd appearance in Chrome and Safari.
|
||||
2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type='search'] {
|
||||
-webkit-appearance: textfield;
|
||||
/* 1 */
|
||||
outline-offset: -2px;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button;
|
||||
/* 1 */
|
||||
font: inherit;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct display in Chrome and Safari.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/*
|
||||
Removes the default spacing and border for appropriate elements.
|
||||
*/
|
||||
|
||||
blockquote,
|
||||
dl,
|
||||
dd,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hr,
|
||||
figure,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
menu {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent resizing textareas horizontally by default.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
|
||||
2. Set the default placeholder color to the user's configured gray 400 color.
|
||||
*/
|
||||
|
||||
input::-moz-placeholder, textarea::-moz-placeholder {
|
||||
opacity: 1;
|
||||
/* 1 */
|
||||
color: #9ca3af;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
opacity: 1;
|
||||
/* 1 */
|
||||
color: #9ca3af;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Set the default cursor for buttons.
|
||||
*/
|
||||
|
||||
button,
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
Make sure disabled buttons don't get the pointer cursor.
|
||||
*/
|
||||
|
||||
:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
|
||||
This can trigger a poorly considered lint error in some tools but is included by design.
|
||||
*/
|
||||
|
||||
img,
|
||||
svg,
|
||||
video,
|
||||
canvas,
|
||||
audio,
|
||||
iframe,
|
||||
embed,
|
||||
object {
|
||||
display: block;
|
||||
/* 1 */
|
||||
vertical-align: middle;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
*/
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Make elements with the HTML hidden attribute stay hidden by default */
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
*, ::before, ::after {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
|
||||
::backdrop {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
|
||||
.mx-4 {
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.my-2 {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.my-3 {
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.mb-12 {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.mt-12 {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.h-4 {
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.min-h-screen {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.w-4 {
|
||||
width: 1rem;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.min-w-0 {
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
.min-w-full {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.max-w-sm {
|
||||
max-width: 24rem;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.truncate {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rounded {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.rounded-md {
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.border-2 {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.border-b {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.bg-gray-200 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-red-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(254 226 226 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.p-2 {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.p-4 {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.p-5 {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.px-4 {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.py-4 {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.pb-2 {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.font-sans {
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
|
||||
.text-4xl {
|
||||
font-size: 2.25rem;
|
||||
line-height: 2.5rem;
|
||||
}
|
||||
|
||||
.text-base {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.font-medium {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.leading-5 {
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.text-red-700 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(185 28 28 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.shadow {
|
||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
|
||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||
}
|
||||
|
||||
.filter {
|
||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
||||
}
|
||||
|
||||
.transition {
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
|
||||
.duration-150 {
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
|
||||
.ease-in-out {
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* Write your own custom component styles here */
|
||||
|
||||
.btn-black {
|
||||
border-radius: 0.25rem;
|
||||
border-width: 1px;
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(0 0 0 / var(--tw-border-opacity));
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.btn-black:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(0 0 0 / var(--tw-text-opacity));
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
|
||||
.btn-black-outline {
|
||||
border-radius: 0.25rem;
|
||||
border-width: 1px;
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(0 0 0 / var(--tw-border-opacity));
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(0 0 0 / var(--tw-text-opacity));
|
||||
}
|
||||
|
||||
.btn-black-outline:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(255 255 255 / var(--tw-text-opacity));
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#__next {
|
||||
height: 100vh;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 4rem;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hover\:border-black:hover {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(0 0 0 / var(--tw-border-opacity));
|
||||
}
|
||||
|
||||
.hover\:bg-gray-200:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.focus\:bg-gray-200:focus {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.focus\:outline-none:focus {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.sm\:h-auto {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.sm\:w-2\/5 {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.sm\:px-6 {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* Write your own custom component styles here */
|
||||
.btn-black {
|
||||
@apply border border-black bg-black text-white font-bold py-2 px-4 rounded text-center;
|
||||
}
|
||||
.btn-black:hover {
|
||||
@apply transition duration-150 bg-white text-black;
|
||||
}
|
||||
.btn-black-outline {
|
||||
@apply border border-black text-black py-2 px-4 rounded font-bold text-center;
|
||||
}
|
||||
.btn-black-outline:hover {
|
||||
@apply transition duration-150 bg-black text-white;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#__next {
|
||||
height: 100vh;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 4rem;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./components/**/*.{js,ts,jsx,tsx}', './pages/**/*.{js,ts,jsx,tsx}'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
'accent-1': '#333',
|
||||
},
|
||||
},
|
||||
},
|
||||
variants: {},
|
||||
plugins: [],
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -457,53 +457,6 @@ video {
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
|
||||
::-webkit-backdrop {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-rotate: 0;
|
||||
--tw-skew-x: 0;
|
||||
--tw-skew-y: 0;
|
||||
--tw-scale-x: 1;
|
||||
--tw-scale-y: 1;
|
||||
--tw-pan-x: ;
|
||||
--tw-pan-y: ;
|
||||
--tw-pinch-zoom: ;
|
||||
--tw-scroll-snap-strictness: proximity;
|
||||
--tw-ordinal: ;
|
||||
--tw-slashed-zero: ;
|
||||
--tw-numeric-figure: ;
|
||||
--tw-numeric-spacing: ;
|
||||
--tw-numeric-fraction: ;
|
||||
--tw-ring-inset: ;
|
||||
--tw-ring-offset-width: 0px;
|
||||
--tw-ring-offset-color: #fff;
|
||||
--tw-ring-color: rgb(59 130 246 / 0.5);
|
||||
--tw-ring-offset-shadow: 0 0 #0000;
|
||||
--tw-ring-shadow: 0 0 #0000;
|
||||
--tw-shadow: 0 0 #0000;
|
||||
--tw-shadow-colored: 0 0 #0000;
|
||||
--tw-blur: ;
|
||||
--tw-brightness: ;
|
||||
--tw-contrast: ;
|
||||
--tw-grayscale: ;
|
||||
--tw-hue-rotate: ;
|
||||
--tw-invert: ;
|
||||
--tw-saturate: ;
|
||||
--tw-sepia: ;
|
||||
--tw-drop-shadow: ;
|
||||
--tw-backdrop-blur: ;
|
||||
--tw-backdrop-brightness: ;
|
||||
--tw-backdrop-contrast: ;
|
||||
--tw-backdrop-grayscale: ;
|
||||
--tw-backdrop-hue-rotate: ;
|
||||
--tw-backdrop-invert: ;
|
||||
--tw-backdrop-opacity: ;
|
||||
--tw-backdrop-saturate: ;
|
||||
--tw-backdrop-sepia: ;
|
||||
}
|
||||
|
||||
::backdrop {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
|
||||
@@ -33,10 +33,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/json-stringify-safe": "^5.0.0",
|
||||
"aws-sdk": "^2.1315.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-custom": "*",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"prettier": "^2.5.1",
|
||||
"prettier": "^2.8.4",
|
||||
"prettier-plugin-sql-cst": "^0.5.0",
|
||||
"ts-jest": "^27.0.7",
|
||||
"turbo": "^1.4.7"
|
||||
},
|
||||
@@ -55,16 +57,5 @@
|
||||
"functions",
|
||||
"database",
|
||||
"auth"
|
||||
],
|
||||
"dependencies": {
|
||||
"@code-hike/mdx": "^0.7.4",
|
||||
"@mdx-js/react": "^2.1.1",
|
||||
"aws-sdk": "^2.1315.0",
|
||||
"mdx-mermaid": "^1.3.2",
|
||||
"mermaid": "^9.2.2",
|
||||
"mini-svg-data-uri": "^1.4.4"
|
||||
},
|
||||
"overrides": {
|
||||
"pgsql-parser": "13.4.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -305,6 +305,7 @@ export * from './src/components/Icon/icons/IconSun'
|
||||
export * from './src/components/Icon/icons/IconSunrise'
|
||||
export * from './src/components/Icon/icons/IconSunset'
|
||||
export * from './src/components/Icon/icons/IconTablet'
|
||||
export * from './src/components/Icon/icons/IconTable'
|
||||
export * from './src/components/Icon/icons/IconTag'
|
||||
export * from './src/components/Icon/icons/IconTarget'
|
||||
export * from './src/components/Icon/icons/IconTerminal'
|
||||
|
||||
@@ -234,6 +234,7 @@ export default {
|
||||
IconSunrise: 'src/components/Icon/icons/IconSunrise/index.tsx',
|
||||
IconSunset: 'src/components/Icon/icons/IconSunset/index.tsx',
|
||||
IconTablet: 'src/components/Icon/icons/IconTablet/index.tsx',
|
||||
IconTable: 'src/components/Icon/icons/IconTable/index.tsx',
|
||||
IconTag: 'src/components/Icon/icons/IconTag/index.tsx',
|
||||
IconTarget: 'src/components/Icon/icons/IconTarget/index.tsx',
|
||||
IconTerminal: 'src/components/Icon/icons/IconTerminal/index.tsx',
|
||||
@@ -284,5 +285,5 @@ export default {
|
||||
IconZapOff: 'src/components/Icon/icons/IconZapOff/index.tsx',
|
||||
IconZap: 'src/components/Icon/icons/IconZap/index.tsx',
|
||||
IconZoomIn: 'src/components/Icon/icons/IconZoomIn/index.tsx',
|
||||
IconZoomOut: 'src/components/Icon/icons/IconZoomOut/index.tsx'
|
||||
}
|
||||
IconZoomOut: 'src/components/Icon/icons/IconZoomOut/index.tsx',
|
||||
}
|
||||
@@ -939,6 +939,10 @@
|
||||
"name": "Tablet",
|
||||
"path": "react-feather/dist/icons/tablet"
|
||||
},
|
||||
"Table": {
|
||||
"name": "Table",
|
||||
"path": "react-feather/dist/icons/table"
|
||||
},
|
||||
"Tag": {
|
||||
"name": "Tag",
|
||||
"path": "react-feather/dist/icons/tag"
|
||||
|
||||
@@ -39,8 +39,8 @@ interface AccordionProps {
|
||||
iconPosition?: Align
|
||||
onChange?: (item: string | string[]) => void
|
||||
openBehaviour: 'single' | 'multiple'
|
||||
type: Type
|
||||
size: Size
|
||||
type?: Type
|
||||
size?: Size
|
||||
defaultValue?: string | string[] | undefined
|
||||
justified?: Boolean
|
||||
chevronAlign?: Align
|
||||
@@ -98,7 +98,7 @@ function Accordion({
|
||||
className={containerClasses.join(' ')}
|
||||
children={
|
||||
<AccordionContext.Provider value={{ ...contextValue }}>
|
||||
<div className={containerClasses.join(' ')}>{children}</div>
|
||||
<div>{children}</div>
|
||||
</AccordionContext.Provider>
|
||||
}
|
||||
></RadixAccordion.Root>
|
||||
|
||||
@@ -173,13 +173,18 @@ interface RadioProps {
|
||||
children: React.ReactNode
|
||||
value: string
|
||||
ItemIndicator?: React.ReactNode
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export function Radio({ children, value, ItemIndicator }: RadioProps) {
|
||||
export function Radio({ children, value, ItemIndicator, disabled }: RadioProps) {
|
||||
let __styles = styleHandler('dropdown')
|
||||
|
||||
return (
|
||||
<RadixDropdown.RadioItem value={value} className={`${__styles.item} ${__styles.input}`}>
|
||||
<RadixDropdown.RadioItem
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
className={`${__styles.item} ${__styles.input}`}
|
||||
>
|
||||
<RadixDropdown.ItemIndicator className={__styles.check}>
|
||||
{ItemIndicator ? ItemIndicator : <IconTarget strokeWidth={6} size={10} />}
|
||||
</RadixDropdown.ItemIndicator>
|
||||
|
||||
@@ -241,6 +241,7 @@ export const Sun: Icon = require(`react-feather/dist/icons/sun`)
|
||||
export const Sunrise: Icon = require(`react-feather/dist/icons/sunrise`)
|
||||
export const Sunset: Icon = require(`react-feather/dist/icons/sunset`)
|
||||
export const Tablet: Icon = require(`react-feather/dist/icons/tablet`)
|
||||
export const Table: Icon = require(`react-feather/dist/icons/table`)
|
||||
export const Tag: Icon = require(`react-feather/dist/icons/tag`)
|
||||
export const Target: Icon = require(`react-feather/dist/icons/target`)
|
||||
export const Terminal: Icon = require(`react-feather/dist/icons/terminal`)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// DO NOT EDIT
|
||||
// this file is generated by /internals/populate-icons script
|
||||
import * as React from 'react'
|
||||
// @ts-ignore
|
||||
import { Table } from 'react-feather'
|
||||
import IconBase from './../../IconBase'
|
||||
|
||||
function IconTable(props: any) {
|
||||
return <IconBase icon={Table} {...props} />
|
||||
}
|
||||
|
||||
export default IconTable
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default as IconTable } from './IconTable'
|
||||
@@ -234,6 +234,7 @@ export * as IconSun from './IconSun'
|
||||
export * as IconSunrise from './IconSunrise'
|
||||
export * as IconSunset from './IconSunset'
|
||||
export * as IconTablet from './IconTablet'
|
||||
export * as IconTable from './IconTable'
|
||||
export * as IconTag from './IconTag'
|
||||
export * as IconTarget from './IconTarget'
|
||||
export * as IconTerminal from './IconTerminal'
|
||||
|
||||
@@ -22,7 +22,7 @@ interface CustomProps {
|
||||
children?: React.ReactNode
|
||||
header?: string | React.ReactNode
|
||||
visible: boolean
|
||||
size?: 'medium' | 'large' | 'xlarge'
|
||||
size?: 'medium' | 'large' | 'xlarge' | 'xxlarge'
|
||||
loading?: boolean
|
||||
align?: 'right' | 'left'
|
||||
hideFooter?: boolean
|
||||
|
||||
@@ -187,7 +187,11 @@ export default {
|
||||
chevron: {
|
||||
base: `
|
||||
text-scale-900
|
||||
rotate-0 group-state-open:rotate-180
|
||||
rotate-0
|
||||
group-state-open:rotate-180
|
||||
group-data-[state=open]:rotate-180
|
||||
ease-[cubic-bezier(0.87,_0,_0.13,_1)]
|
||||
transition-transform duration-300
|
||||
duration-200
|
||||
`,
|
||||
align: {
|
||||
@@ -998,6 +1002,7 @@ export default {
|
||||
medium: `w-screen max-w-md h-full`,
|
||||
large: `w-screen max-w-2xl h-full`,
|
||||
xlarge: `w-screen max-w-3xl h-full`,
|
||||
xxlarge: `w-screen max-w-4xl h-full`,
|
||||
},
|
||||
align: {
|
||||
left: `
|
||||
|
||||