Merge branch 'master' into poc/improve-join-the-community
No files matched your search
@@ -67,7 +67,7 @@ You can also [self-host](https://supabase.com/docs/guides/hosting/overview) and
|
||||
- [pg_graphql](http://github.com/supabase/pg_graphql/) a PostgreSQL extension that exposes a GraphQL API
|
||||
- [Storage](https://github.com/supabase/storage-api) provides a RESTful interface for managing Files stored in S3, using Postgres to manage permissions.
|
||||
- [postgres-meta](https://github.com/supabase/postgres-meta) is a RESTful API for managing your Postgres, allowing you to fetch tables, add roles, and run queries, etc.
|
||||
- [GoTrue](https://github.com/supabase/gotrue) is an JWT based API for managing users and issuing JWT tokens.
|
||||
- [GoTrue](https://github.com/supabase/gotrue) is a JWT based API for managing users and issuing JWT tokens.
|
||||
- [Kong](https://github.com/Kong/kong) is a cloud-native API gateway.
|
||||
|
||||
#### Client libraries
|
||||
|
||||
@@ -61,13 +61,16 @@ as $$
|
||||
declare
|
||||
status int;
|
||||
content text;
|
||||
avatar_name text;
|
||||
begin
|
||||
if coalesce(old.avatar_url, '') <> ''
|
||||
and (tg_op = 'DELETE' or (old.avatar_url <> new.avatar_url)) then
|
||||
-- extract avatar name
|
||||
avatar_name := substring(old.avatar_url from '/([^\/]+)\?.*$');
|
||||
select
|
||||
into status, content
|
||||
result.status, result.content
|
||||
from public.delete_avatar(old.avatar_url) as result;
|
||||
from public.delete_avatar(avatar_name) as result;
|
||||
if status <> 200 then
|
||||
raise warning 'Could not delete avatar: % %', status, content;
|
||||
end if;
|
||||
|
||||
@@ -495,22 +495,36 @@ export const auth = {
|
||||
{ name: 'Managing User Data', url: '/guides/auth/managing-user-data' },
|
||||
{ name: 'Multi-Factor Authentication', url: '/guides/auth/auth-mfa' },
|
||||
{ name: 'Row Level Security', url: '/guides/auth/row-level-security' },
|
||||
{ name: 'Server-side Rendering', url: '/guides/auth/server-side-rendering' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Auth Helpers',
|
||||
name: 'Server-side Auth',
|
||||
url: undefined,
|
||||
items: [
|
||||
{ name: 'Overview', url: '/guides/auth/auth-helpers' },
|
||||
{ name: 'Auth UI', url: '/guides/auth/auth-helpers/auth-ui' },
|
||||
{ name: 'Flutter Auth UI', url: '/guides/auth/auth-helpers/flutter-auth-ui' },
|
||||
{
|
||||
name: 'Next.js',
|
||||
url: '/guides/auth/auth-helpers/nextjs',
|
||||
},
|
||||
{ name: 'Remix', url: '/guides/auth/auth-helpers/remix' },
|
||||
{ name: 'SvelteKit', url: '/guides/auth/auth-helpers/sveltekit' },
|
||||
{ name: 'Server-side Rendering', url: '/guides/auth/server-side-rendering' },
|
||||
{
|
||||
name: 'Email Auth with PKCE flow for SSR',
|
||||
url: '/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr',
|
||||
},
|
||||
{
|
||||
name: 'OAuth with PKCE flow for SSR',
|
||||
url: '/guides/auth/server-side/oauth-with-pkce-flow-for-ssr',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Auth UI',
|
||||
url: undefined,
|
||||
items: [
|
||||
{ name: 'Auth UI', url: '/guides/auth/auth-helpers/auth-ui' },
|
||||
{ name: 'Flutter Auth UI', url: '/guides/auth/auth-helpers/flutter-auth-ui' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -244,7 +244,7 @@ const Container = memo(function Container(props) {
|
||||
className={[
|
||||
// 'overflow-x-auto',
|
||||
'w-full h-screen transition-all ease-out',
|
||||
'absolute lg:relative',
|
||||
// 'absolute lg:relative',
|
||||
mobileMenuOpen
|
||||
? '!w-auto ml-[75%] sm:ml-[50%] md:ml-[33%] overflow-hidden'
|
||||
: 'overflow-auto',
|
||||
|
||||
@@ -2,31 +2,15 @@ import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'index',
|
||||
title: 'Auth Helpers Overview',
|
||||
description: 'A collection of framework-specific Auth utilities for working with Supabase.',
|
||||
title: 'Server-Side Auth Overview',
|
||||
description: 'Server-Side Auth guides and utilities for working with Supabase.',
|
||||
sidebar_label: 'Overview',
|
||||
}
|
||||
|
||||
A collection of framework-specific Auth utilities for working with Supabase.
|
||||
Working with server-side frameworks is slightly different to client-side frameworks. In this section we cover the various ways of handling server-side authentication and demonstrate how to use the Supabase helper-libraries to make the process more seamless.
|
||||
|
||||
<div className="container" style={{ padding: 0 }}>
|
||||
<div className="grid md:grid-cols-12 gap-4">
|
||||
{/* Auth UI */}
|
||||
<div className="col-span-6">
|
||||
<ButtonCard
|
||||
to={'/guides/auth/auth-helpers/auth-ui'}
|
||||
title={'Auth UI'}
|
||||
description={'A pre-built React component for authenticating users.'}
|
||||
/>
|
||||
</div>
|
||||
{/* Flutter Auth UI */}
|
||||
<div className="col-span-6">
|
||||
<ButtonCard
|
||||
to={'/guides/auth/auth-helpers/flutter-auth-ui'}
|
||||
title={'Flutter Auth UI'}
|
||||
description={'Pre-built Flutter widgets for authenticating users.'}
|
||||
/>
|
||||
</div>
|
||||
{/* Next.js */}
|
||||
<div className="col-span-6">
|
||||
<ButtonCard
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
title: 'Email Auth with PKCE flow for SSR',
|
||||
description:
|
||||
'Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.',
|
||||
subtitle:
|
||||
'Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.',
|
||||
}
|
||||
|
||||
### Install Supabase Auth Helpers
|
||||
|
||||
The Auth Helpers will assist you in implementing user authentication within your server-side rendering (SSR) framework.
|
||||
|
||||
<Tabs scrollable size="small" type="underlined" defaultActiveId="nextjs">
|
||||
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
|
||||
```bash
|
||||
npm install @supabase/auth-helpers-nextjs @supabase/supabase-js
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
```bash
|
||||
npm install @supabase/auth-helpers-sveltekit @supabase/supabase-js
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Set environment variables
|
||||
|
||||
Create an `.env.local` file in your project root directory. You can get your `SITE_URL` and `ANON_KEY` from inside of the [dashboard](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
<Tabs scrollable size="small" type="underlined" defaultActiveId="nextjs">
|
||||
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
|
||||
```bash .env.local
|
||||
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
```bash .env.local
|
||||
PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Setting up the Auth Helpers
|
||||
|
||||
When using the Supabase client on the server, you must perform extra steps to ensure the user's auth session remains active. Since the user's session is tracked in a cookie, we need to read this cookie and update it if necessary.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
Next.js Server Components allow you to read a cookie but not write back to it. Middleware on the other hand allow you to both read and write to cookies.
|
||||
|
||||
Next.js [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) runs immediately before each route is rendered. We'll use Middleware to refresh the user's session before loading Server Component routes.
|
||||
|
||||
Create a new `middleware.js` file in the root of your project and populate with the following:
|
||||
|
||||
```js middleware.js
|
||||
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function middleware(req) {
|
||||
const res = NextResponse.next()
|
||||
const supabase = createMiddlewareClient({ req, res })
|
||||
await supabase.auth.getSession()
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
Create a new `hooks.server.js` file in the root of your project and populate with the following:
|
||||
|
||||
```ts src/hooks.server.js
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { Handle } from '@sveltejs/kit'
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.supabase = createSupabaseServerClient({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event,
|
||||
})
|
||||
|
||||
event.locals.getSession = async () => {
|
||||
const {
|
||||
data: { session },
|
||||
} = await event.locals.supabase.auth.getSession()
|
||||
return session
|
||||
}
|
||||
|
||||
return resolve(event, {
|
||||
filterSerializedResponseHeaders(name) {
|
||||
return name === 'content-range'
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Create API endpoint for handling `token_hash`
|
||||
|
||||
In order to use the updated email links we will need to setup a endpoint for verifying the `token_hash` along with the `type` to exchange `token_hash` for the user's `session`, which is set as a cookie for future requests made to Supabase.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
Create a new file at `app/auth/confirm/route.js` and populate with the following:
|
||||
|
||||
```js app/auth/confirm/route.js
|
||||
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { cookies } from 'next/headers'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(req) {
|
||||
const { searchParams } = new URL(req.url)
|
||||
const token_hash = searchParams.get('token_hash')
|
||||
const type = searchParams.get('type')
|
||||
const next = searchParams.get('next') ?? '/'
|
||||
|
||||
if (token_hash && type) {
|
||||
const supabase = createRouteHandlerClient({ cookies })
|
||||
const { error } = await supabase.auth.verifyOtp({ type, token_hash })
|
||||
if (!error) {
|
||||
return NextResponse.redirect(new URL(`/${next.slice(1)}`, req.url))
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
return NextResponse.redirect(new URL('/auth/auth-code-error', req.url))
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
Create a new file at `src/routes/auth/confirm/+server.js` and populate with the following:
|
||||
|
||||
```js src/routes/auth/confirm/+server.js
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
export const GET = async (event) => {
|
||||
const {
|
||||
url,
|
||||
locals: { supabase }
|
||||
} = event;
|
||||
const token_hash = url.searchParams.get('token') as string;
|
||||
const type = url.searchParams.get('type') as string;
|
||||
const next = url.searchParams.get('next') ?? '/';
|
||||
|
||||
if (token_hash && type) {
|
||||
const { error } = await supabase.auth.verifyOtp({ token_hash, type });
|
||||
if (!error) {
|
||||
throw redirect(303, `/${next.slice(1)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
throw redirect(303, '/auth/auth-code-error');
|
||||
};
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Update email templates with URL for API endpoint
|
||||
|
||||
Let's update the URL in our email templates to point to our new confirmation endpoint for the user to get confirmed.
|
||||
|
||||
**Confirm signup template**
|
||||
|
||||
```html
|
||||
<h2>Confirm your signup</h2>
|
||||
|
||||
<p>Follow this link to confirm your user:</p>
|
||||
<p>
|
||||
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email"
|
||||
>Confirm your email</a
|
||||
>
|
||||
</p>
|
||||
```
|
||||
|
||||
**Invite user template**
|
||||
|
||||
```html
|
||||
<h2>You have been invited</h2>
|
||||
|
||||
<p>
|
||||
You have been invited to create a user on {{ .SiteURL }}. Follow this link to accept the invite:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a
|
||||
href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=invite&next=/path-to-your-update-password-page"
|
||||
>Accept the invite</a
|
||||
>
|
||||
</p>
|
||||
```
|
||||
|
||||
**Magic Link template**
|
||||
|
||||
```html
|
||||
<h2>Magic Link</h2>
|
||||
|
||||
<p>Follow this link to login:</p>
|
||||
<p><a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email">Log In</a></p>
|
||||
```
|
||||
|
||||
**Change Email Address template**
|
||||
|
||||
```html
|
||||
<h2>Confirm Change of Email</h2>
|
||||
|
||||
<p>Follow this link to confirm the update of your email from {{ .Email }} to {{ .NewEmail }}:</p>
|
||||
<p><a href="{{ .ConfirmationURL }}">Change Email</a></p>
|
||||
```
|
||||
|
||||
**Reset Password template**
|
||||
|
||||
```html
|
||||
<h2>Reset Password</h2>
|
||||
|
||||
<p>Follow this link to reset the password for your user:</p>
|
||||
<p>
|
||||
<a
|
||||
href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=recovery&next=/path-to-your-update-password-page"
|
||||
>Reset Password</a
|
||||
>
|
||||
</p>
|
||||
```
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -0,0 +1,207 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
title: 'OAuth with PKCE flow for SSR',
|
||||
description:
|
||||
'Learn how to configure OAuth authentication in your server-side rendering (SSR) application to work with the PKCE flow.',
|
||||
subtitle:
|
||||
'Learn how to configure OAuth authentication in your server-side rendering (SSR) application to work with the PKCE flow.',
|
||||
}
|
||||
|
||||
### Install Supabase Auth Helpers
|
||||
|
||||
The Auth Helpers assist with user authentication within server-side rendering (SSR) frameworks.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
|
||||
```bash
|
||||
npm install @supabase/auth-helpers-nextjs @supabase/supabase-js
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
```bash
|
||||
npm install @supabase/auth-helpers-sveltekit @supabase/supabase-js
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Set environment variables
|
||||
|
||||
Create an `.env.local` file in your project root directory. You can get your `SITE_URL` and `ANON_KEY` from inside of the [dashboard](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
|
||||
```bash .env.local
|
||||
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
```bash .env.local
|
||||
PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Setting up the Auth Helpers
|
||||
|
||||
For SSR, the Supabase client requires extra steps to ensure the user's auth session remains active. Since the user's session is tracked in a cookie, we need to read this cookie and update it if necessary.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
Next.js Server Components allow you to read a cookie but not write back to it. Middleware on the other hand allow you to both read and write to cookies.
|
||||
|
||||
Next.js [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) runs immediately before each route is rendered. We'll use Middleware to refresh the user's session before loading Server Component routes.
|
||||
|
||||
Create a new `middleware.js` file in the root of your project and populate with the following:
|
||||
|
||||
```js middleware.js
|
||||
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function middleware(req) {
|
||||
const res = NextResponse.next()
|
||||
const supabase = createMiddlewareClient({ req, res })
|
||||
await supabase.auth.getSession()
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
Create a new `hooks.server.js` file in the root of your project and populate with the following:
|
||||
|
||||
```ts src/hooks.server.js
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { Handle } from '@sveltejs/kit'
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.supabase = createSupabaseServerClient({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event,
|
||||
})
|
||||
|
||||
event.locals.getSession = async () => {
|
||||
const {
|
||||
data: { session },
|
||||
} = await event.locals.supabase.auth.getSession()
|
||||
return session
|
||||
}
|
||||
|
||||
return resolve(event, {
|
||||
filterSerializedResponseHeaders(name) {
|
||||
return name === 'content-range'
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Create API endpoint for handling the `code` exchange
|
||||
|
||||
In order to use OAuth we will need to setup a endpoint for the `code` exchange, to exchange an auth `code` for the user's `session`, which is set as a cookie for future requests made to Supabase.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
Create a new file at `app/auth/callback/route.js` and populate with the following:
|
||||
|
||||
```js app/auth/callback/route.js
|
||||
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { cookies } from 'next/headers'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(req) {
|
||||
const { searchParams } = new URL(req.url)
|
||||
const code = searchParams.get('code')
|
||||
const next = searchParams.get('next') ?? '/'
|
||||
|
||||
if (code) {
|
||||
const supabase = createRouteHandlerClient({ cookies: () => cookies() })
|
||||
const { error } = await supabase.auth.exchangeCodeForSession(code)
|
||||
if (!error) {
|
||||
return NextResponse.redirect(new URL(`/${next.slice(1)}`, req.url))
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with instructions
|
||||
return NextResponse.redirect(new URL('/auth/auth-code-error', req.url))
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
Create a new file at `src/routes/auth/callback/+server.js` and populate with the following:
|
||||
|
||||
```js src/routes/auth/callback/+server.js
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
export const GET = async (event) => {
|
||||
const {
|
||||
url,
|
||||
locals: { supabase }
|
||||
} = event;
|
||||
const code = url.searchParams.get('code') as string;
|
||||
const next = url.searchParams.get('next') ?? '/';
|
||||
|
||||
if (code) {
|
||||
const { error } = await supabase.auth.exchangeCodeForSession(code)
|
||||
if (!error) {
|
||||
throw redirect(303, `/${next.slice(1)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with instructions
|
||||
throw redirect(303, '/auth/auth-code-error');
|
||||
};
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
Let's point our `.signInWithOAuth` method's redirect to the callback route we create above:
|
||||
|
||||
```js
|
||||
await supabase.auth.signInWithOAuth({
|
||||
email,
|
||||
options: {
|
||||
redirectTo: `http://example.com/auth/callback`,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -8,21 +8,23 @@ export const meta = {
|
||||
|
||||
Every project on the Supabase Platform comes with its own dedicated Postgres instance running inside a virtual machine (VM). The following table describes the base instance with additional compute add-ons available if you need extra performance when scaling up Supabase.
|
||||
|
||||
| Plan | Pricing | CPU | Memory | Connections: Direct | Connections: Pooler |
|
||||
| --------------- | ------- | ----------------------- | ------ | ------------------- | ------------------- |
|
||||
| Free (Included) | $0 | 2-core ARM (shared) | 1 GB | 60 | 200 |
|
||||
| Small | $5 | 2-core ARM (shared) | 2 GB | 90 | 200 |
|
||||
| Medium | $50 | 2-core ARM (shared) | 4 GB | 120 | 200 |
|
||||
| Large | $100 | 2-core ARM (dedicated) | 8 GB | 160 | 300 |
|
||||
| XL | $200 | 4-core ARM (dedicated) | 16 GB | 240 | 700 |
|
||||
| 2XL | $400 | 8-core ARM (dedicated) | 32 GB | 380 | 1500 |
|
||||
| 4XL | $950 | 16-core ARM (dedicated) | 64 GB | 480 | 3000 |
|
||||
| 8XL | $1,860 | 32-core ARM (dedicated) | 128 GB | 490 | 6000 |
|
||||
| 12XL | $2,790 | 48-core ARM (dedicated) | 192 GB | 500 | 9000 |
|
||||
| 16XL | $3,720 | 64-core ARM (dedicated) | 256 GB | 500 | 12,000 |
|
||||
| Plan | Hourly Price USD | Monthly Price USD | CPU | Memory | Connections: Direct | Connections: Pooler |
|
||||
| ------- | ---------------- | ----------------- | ----------------------- | ------ | ------------------- | ------------------- |
|
||||
| Starter | $0.01344 | ~$10 | 2-core ARM (shared) | 1 GB | 60 | 200 |
|
||||
| Small | $0.0206 | ~$15 | 2-core ARM (shared) | 2 GB | 90 | 200 |
|
||||
| Medium | $0.0822 | ~$60 | 2-core ARM (shared) | 4 GB | 120 | 200 |
|
||||
| Large | $0.1517 | ~$110 | 2-core ARM (dedicated) | 8 GB | 160 | 300 |
|
||||
| XL | $0.2877 | ~$210 | 4-core ARM (dedicated) | 16 GB | 240 | 700 |
|
||||
| 2XL | $0.562 | ~$410 | 8-core ARM (dedicated) | 32 GB | 380 | 1500 |
|
||||
| 4XL | $1.32 | ~$960 | 16-core ARM (dedicated) | 64 GB | 480 | 3000 |
|
||||
| 8XL | $2.562 | ~$1,870 | 32-core ARM (dedicated) | 128 GB | 490 | 6000 |
|
||||
| 12XL | $3.836 | ~$2,800 | 48-core ARM (dedicated) | 192 GB | 500 | 9000 |
|
||||
| 16XL | $5.12 | ~$3,730 | 64-core ARM (dedicated) | 256 GB | 500 | 12,000 |
|
||||
|
||||
Number of connections above are recommended values.
|
||||
|
||||
We charge hourly for additional compute based on your usage. Read more about [usage-based billing for compute](/docs/guides/platform/org-based-billing#usage-based-billing-for-compute).
|
||||
|
||||
[Contact us](https://supabase.com/contact/enterprise) if you require a custom plan.
|
||||
|
||||
## Dedicated vs. shared CPU
|
||||
@@ -37,18 +39,18 @@ When considering compute upgrades, assess whether your bottlenecks are hardware-
|
||||
|
||||
SSD Disks are attached to your servers and the disk performance depends on the compute add-on of your instance. Disk IO refers to two metrics: throughput (Megabits per Second) and IOPS (Input/Output Operations per Second).
|
||||
|
||||
| Plan | Pricing | Max Disk Throughput | Baseline Disk Throughput | Max IOPS | Baseline IOPS |
|
||||
| --------------- | ------- | ------------------- | ------------------------ | ----------- | ------------- |
|
||||
| Free (Included) | $0 | 2,085 Mbps | 87 Mbps | 11,800 IOPS | 500 IOPS |
|
||||
| Small | $5 | 2,085 Mbps | 174 Mbps | 11,800 IOPS | 1,000 IOPS |
|
||||
| Medium | $50 | 2,085 Mbps | 347 Mbps | 11,800 IOPS | 2,000 IOPS |
|
||||
| Large | $100 | 4,750 Mbps | 630 Mbps | 20,000 IOPS | 3,600 IOPS |
|
||||
| XL | $200 | 4,750 Mbps | 1,188 Mbps | 20,000 IOPS | 6,000 IOPS |
|
||||
| 2XL | $400 | 4,750 Mbps | 2,375 Mbps | 20,000 IOPS | 12,000 IOPS |
|
||||
| 4XL | $950 | 4,750 Mbps | 4,750 Mbps | 20,000 IOPS | 20,000 IOPS |
|
||||
| 8XL | $1,860 | 9,500 Mbps | 9,500 Mbps | 40,000 IOPS | 40,000 IOPS |
|
||||
| 12XL | $2,790 | 14,250 Mbps | 14,250 Mbps | 50,000 IOPS | 50,000 IOPS |
|
||||
| 16XL | $3,720 | 19,000 Mbps | 19,000 Mbps | 80,000 IOPS | 80,000 IOPS |
|
||||
| Plan | Max Disk Throughput | Baseline Disk Throughput | Max IOPS | Baseline IOPS |
|
||||
| ------- | ------------------- | ------------------------ | ----------- | ------------- |
|
||||
| Starter | 2,085 Mbps | 87 Mbps | 11,800 IOPS | 500 IOPS |
|
||||
| Small | 2,085 Mbps | 174 Mbps | 11,800 IOPS | 1,000 IOPS |
|
||||
| Medium | 2,085 Mbps | 347 Mbps | 11,800 IOPS | 2,000 IOPS |
|
||||
| Large | 4,750 Mbps | 630 Mbps | 20,000 IOPS | 3,600 IOPS |
|
||||
| XL | 4,750 Mbps | 1,188 Mbps | 20,000 IOPS | 6,000 IOPS |
|
||||
| 2XL | 4,750 Mbps | 2,375 Mbps | 20,000 IOPS | 12,000 IOPS |
|
||||
| 4XL | 4,750 Mbps | 4,750 Mbps | 20,000 IOPS | 20,000 IOPS |
|
||||
| 8XL | 9,500 Mbps | 9,500 Mbps | 40,000 IOPS | 40,000 IOPS |
|
||||
| 12XL | 14,250 Mbps | 14,250 Mbps | 50,000 IOPS | 50,000 IOPS |
|
||||
| 16XL | 19,000 Mbps | 19,000 Mbps | 80,000 IOPS | 80,000 IOPS |
|
||||
|
||||
[Contact us](https://supabase.com/contact/enterprise) if you require a custom plan.
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ export const meta = {
|
||||
sidebar_label: 'Videos',
|
||||
}
|
||||
|
||||
In this guide we explore the best ways to receive realtime Postgres changes with your Next.js application.
|
||||
We'll show both client and serverside updates, and explore the which option is best.
|
||||
In this guide, we explore the best ways to receive real-time Postgres changes with your Next.js application.
|
||||
We'll show both client and server side updates, and explore which option is best.
|
||||
|
||||
<div className="video-container">
|
||||
<iframe
|
||||
|
||||
@@ -82,7 +82,7 @@ on storage.objects
|
||||
for select
|
||||
to public
|
||||
using (
|
||||
storage.filename(name)) = 'favicon.ico'
|
||||
storage.filename(name) = 'favicon.ico'
|
||||
);
|
||||
```
|
||||
|
||||
@@ -100,7 +100,7 @@ on storage.objects
|
||||
for insert
|
||||
to authenticated
|
||||
with check (
|
||||
storage.foldername(name))[1] = 'private'
|
||||
storage.foldername(name)[1] = 'private'
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
@@ -378,11 +378,13 @@ The OpenAI API supports [completion streaming](https://platform.openai.com/docs/
|
||||
|
||||
Storing embeddings in Postgres opens a world of possibilities. You can combine your search function with telemetry functions, add an user-provided feedback (thumbs up/down), and make your search feel more integrated with your products.
|
||||
|
||||
The [pgvector extension](https://supabase.com/docs/guides/ai/vector-columns) is available on all new Supabase projects today. If you want to try it out, launch a new Postgres database today: [database.new](https://database.new)
|
||||
The [pgvector extension](https://supabase.com/docs/guides/ai/vector-columns) is available on all new Supabase projects today. To try it out, launch a new Postgres database: [database.new](https://database.new)
|
||||
|
||||
## More pgvector and ChatGPT resources
|
||||
## More pgvector and AI resources
|
||||
|
||||
- [Supabase Clippy: ChatGPT for Supabase Docs](https://supabase.com/blog/chatgpt-supabase-docs)
|
||||
- [A ChatGPT Plugins Template built with Supabase Edge Runtime](https://supabase.com/blog/building-chatgpt-plugins-template)
|
||||
- [Template for building your own custom ChatGPT style doc search](https://github.com/supabase-community/nextjs-openai-doc-search)
|
||||
- [Supabase + LangChain Starter Template](https://blog.langchain.dev/langchain-x-supabase/)
|
||||
- [Hugging Face is now supported in Supabase](https://supabase.com/blog/hugging-face-supabase)
|
||||
- [How to build ChatGPT Plugin from scratch with Supabase Edge Runtime](https://supabase.com/blog/building-chatgpt-plugins-template)
|
||||
- [Docs pgvector: Embeddings and vector similarity](https://supabase.com/docs/guides/database/extensions/pgvector)
|
||||
- [Choosing Compute Add-on for AI workloads](https://supabase.com/docs/guides/ai/choosing-compute-addon)
|
||||
- [pgvector v0.5.0: Faster semantic search with HNSW indexes](https://supabase.com/blog/pgvector-v0-5-0-hnsw)
|
||||
@@ -274,8 +274,7 @@ In a next step you can add authentication to your plugin, let us know on [Twitte
|
||||
|
||||
## More AI resources
|
||||
|
||||
- [Hugging Face is now supported in Supabase](https://supabase.com/blog/hugging-face-supabase)
|
||||
- [pgvector v0.5.0: Faster semantic search with HNSW indexes](https://supabase.com/blog/pgvector-v0-5-0-hnsw)
|
||||
- [OpenAI ChatGPT Plugin docs](https://platform.openai.com/docs/plugins/introduction)
|
||||
- [Advanced plugin template](https://github.com/openai/chatgpt-retrieval-plugin)
|
||||
- Learn to build your own ChatGPT-style docs search with [Deno Fresh](https://deno.com/blog/build-chatgpt-doc-search-with-supabase-fresh) or [Next.js](https://vercel.com/templates/next.js/nextjs-openai-doc-search-starter)
|
||||
- [How we builtChatGPT for the Supabase Docs](https://supabase.com/blog/chatgpt-supabase-docs).
|
||||
- [Docs pgvector: Embeddings and vector similarity](https://supabase.com/docs/guides/database/extensions/pgvector)
|
||||
@@ -15,6 +15,14 @@ image: 2023-07-13-pgvector-performance/vector-benchmarks-og.jpeg
|
||||
thumb: 2023-07-13-pgvector-performance/vector-benchmarks-thumb.jpeg
|
||||
---
|
||||
|
||||
<div className="bg-gray-300 rounded-lg px-6 py-2 bold">
|
||||
|
||||
🚀 The incorporation of the HNSW index in pgvector v0.5.0 ensures lightning-fast vector searches. We tested it and benchmarked and share everything.
|
||||
[Read the new post](https://supabase.com/blog/pgvector-v0-5-0-hnsw)
|
||||
|
||||
</div>
|
||||
pgvector v0.5.0: Faster semantic search with HNSW indexes
|
||||
|
||||
There are a few pgvector benchmarks floating around the internet, most recently a [pgvector vs Qdrant](https://nirantk.com/writing/pgvector-vs-qdrant/) comparison by NirantK. We wanted to reproduce (or improve!) the results.
|
||||
|
||||
There is an obvious bias here: we're a Postgres company. It's not our goal to prove that pgvector is better than Qdrant for running vector workloads. From everything we hear about Qdrant, it's fantastic.
|
||||
@@ -87,7 +95,7 @@ Let's start with NirantK's results as a baseline:
|
||||
/>
|
||||
</div>
|
||||
|
||||
They aren't very flattering! Repeating our statements above, these benchmarks are using the defaults for both engines. Our goal now is to replicate the results, and then see what improvements need to be made as developers scale up their workload.
|
||||
They aren't very flattering! Repeating our statements above, these benchmarks use the defaults for both engines. Our goal now is to replicate the results, and then see what improvements need to be made as developers scale up their workload.
|
||||
|
||||
## Results
|
||||
|
||||
@@ -301,7 +309,8 @@ pgvector is still early in development. As with any open source tool, it needs t
|
||||
|
||||
What's next on the roadmap? Andrew has an impressive list of features [planned for v0.5.0](https://github.com/pgvector/pgvector/issues/27):
|
||||
|
||||
- Adding HNSW: an index with better speed & precision than IVFFlat (at a higher memory cost)
|
||||
✅ Adding HNSW: an index with better speed & precision than IVFFlat (at a higher memory cost)
|
||||
|
||||
- Product quantization: better storage for IVFFLAT, improving speed and recall
|
||||
- Parallel index builds: building your IVFFLAT indexes will be much faster
|
||||
|
||||
@@ -311,3 +320,4 @@ What's next on the roadmap? Andrew has an impressive list of features [planned f
|
||||
- [Docs pgvector: Embeddings and vector similarity](https://supabase.com/docs/guides/database/extensions/pgvector)
|
||||
- [pgvector vs Qdrant](https://nirantk.com/writing/pgvector-vs-qdrant)
|
||||
- [Choosing Compute Add-on for AI workloads](https://supabase.com/docs/guides/ai/choosing-compute-addon)
|
||||
- [pgvector v0.5.0: Faster semantic search with HNSW indexes](https://supabase.com/blog/pgvector-v0-5-0-hnsw)
|
||||
@@ -148,7 +148,7 @@ Ultimately the choice of embedding model will depend on your specific use case a
|
||||
- **Dimension size:** What is the operational cost of storing and comparing embeddings?
|
||||
- **Language:** Which written languages do you need to support? Many of the smaller models only support English.
|
||||
|
||||
The goal is to maximize similarity performance and sequence length, while minimizing model size and dimension size. Notably, if we can find a model that performs well while producing as few dimensions as possible, we can immediately improve many of the above challenges developers are facing with pgvector.
|
||||
The goal is to maximize similarity performance and sequence length while minimizing model size and dimension size. Notably, if we can find a model that performs well while producing as few dimensions as possible, we can immediately improve many of the above challenges developers are facing with pgvector.
|
||||
|
||||
For example [gte-small](https://huggingface.co/thenlper/gte-small), a model recently trained by the [Alibaba DAMO Academy,](https://damo.alibaba.com/) produces only 384 dimensions while ranking higher than `text-embedding-ada-002` on the MTEB leaderboard.
|
||||
|
||||
@@ -162,10 +162,18 @@ It's important to keep in mind that using an alternative embedding model doesn't
|
||||
|
||||
While dimensionality reduction techniques like Principal Component Analysis ([PCA](https://en.wikipedia.org/wiki/Principal_component_analysis)) or t-Distributed Stochastic Neighbor Embedding ([t-SNE](https://en.wikipedia.org/wiki/T-distributed_stochastic_neighbor_embedding)) may seem like a good alternative to trim down the size of text embedding vectors, there is some risk to consider.
|
||||
|
||||
First, these tools may not appropriately model the relationships in high dimensional space. Both PCA and t-SNE are likely to oversimplify the multivariate relationships among different dimensions, leading to loss of important semantic information. This is particularly the case with PCA, which assumes linearity and would fail to preserve nonlinear interactions found in high-dimensional text embeddings.
|
||||
First, these tools may not appropriately model the relationships in high-dimensional space. Both PCA and t-SNE are likely to oversimplify the multivariate relationships among different dimensions, leading to the loss of important semantic information. This is particularly the case with PCA, which assumes linearity and would fail to preserve nonlinear interactions found in high-dimensional text embeddings.
|
||||
|
||||
Additionally, the validity of the dimensionality reduction models could change over time if the distribution of text data shifts. If the model was trained on older data, it might not accurately reflect or accommodate these shifts. For example, if your company gets a large new customer in the legal field and your embedding dataset contained no legal documents when the dimensionality reduction model was trained, otherwise exploitable structure may be destructively compressed. Both PCA and t-SNE are not inherently adaptable to such changes, which could result in an increasingly poor fit over time. In summary, the derived low-dimensional representations may not maintain semantic coherence of the original high-dimensional vectors.
|
||||
|
||||
## What's next?
|
||||
|
||||
At Supabase we are working on ways to make it easy to generate embeddings using open source, high performance, and low dimension embedding models. Stay tuned for [Launch Week 8](/launch-week) next week!
|
||||
|
||||
## More pgvector and AI resources
|
||||
|
||||
- [Hugging Face is now supported in Supabase](https://supabase.com/blog/hugging-face-supabase)
|
||||
- [How to build ChatGPT Plugin from scratch with Supabase Edge Runtime](https://supabase.com/blog/building-chatgpt-plugins-template)
|
||||
- [Docs pgvector: Embeddings and vector similarity](https://supabase.com/docs/guides/database/extensions/pgvector)
|
||||
- [Choosing Compute Add-on for AI workloads](https://supabase.com/docs/guides/ai/choosing-compute-addon)
|
||||
- [pgvector v0.5.0: Faster semantic search with HNSW indexes](https://supabase.com/blog/pgvector-v0-5-0-hnsw)
|
||||
@@ -30,7 +30,7 @@ We've been documenting Kevin's updates to our billing system while it's being us
|
||||
|
||||
## Moving from project-based to org-based billing
|
||||
|
||||
Supabase has organizations and projects. An organization may contain multiple projects. A project is a Supabase instance. Preivously, each project had an individual subscription, plan and add-ons.
|
||||
Supabase has organizations and projects. An organization may contain multiple projects. A project is a Supabase instance. Previously, each project had an individual subscription, plan and add-ons.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { PropsWithChildren, useEffect, useRef } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { detectBrowser } from 'common'
|
||||
|
||||
interface Props {
|
||||
outerClassName?: string
|
||||
@@ -28,7 +29,7 @@ const Panel = ({
|
||||
const trackCursor = hasShimmer || hasInnerShimmer
|
||||
|
||||
const handleGlow = (event: any) => {
|
||||
if (!trackCursor || !outerRef.current || !innerRef.current) return null
|
||||
if (!outerRef.current || !innerRef.current) return null
|
||||
|
||||
const outerElement = outerRef.current as HTMLDivElement
|
||||
const innerElement = innerRef.current as HTMLDivElement
|
||||
@@ -58,7 +59,7 @@ const Panel = ({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return
|
||||
if (typeof window === 'undefined' || !trackCursor) return
|
||||
|
||||
window.addEventListener('mousemove', handleGlow)
|
||||
return () => {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { Fragment, useMemo } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTheme } from 'common/Providers'
|
||||
import { IconCheckCircle, IconXCircle, Modal } from 'ui'
|
||||
import { IconXCircle, Modal } from 'ui'
|
||||
import pricingAddOn from '~/data/PricingAddOnTable.json'
|
||||
import { IconPricingIncludedCheck, IconPricingMinus } from './PricingIcons'
|
||||
import Link from 'next/link'
|
||||
|
||||
interface Props {
|
||||
showComputeModal: boolean
|
||||
@@ -47,12 +48,33 @@ export default function ComputePricingModal({ showComputeModal, setShowComputeMo
|
||||
</div>
|
||||
<div className="max-w-4xl prose">
|
||||
<h2 className="text-lg">Choose best compute setup for you</h2>
|
||||
<p className="text-sm lg:max-w-3xl">
|
||||
<p className="text-sm">
|
||||
Every project on the Supabase Platform comes with its own dedicated Postgres
|
||||
instance running inside a virtual machine (VM). The following table describes the
|
||||
base instance with additional compute add-ons available if you need extra
|
||||
performance when scaling up Supabase.
|
||||
</p>
|
||||
<p className="text-sm">
|
||||
Compute instances are billed hourly and you can scale up or down at any time if you
|
||||
need extra performance. You'll only be charged at the end of the month for the hours
|
||||
you've used. Paid plans come with $10 in Compute Credits to cover one Starter
|
||||
instance or parts of any other instance. Read more on{' '}
|
||||
<Link
|
||||
href="https://supabase.com/docs/guides/platform/org-based-billing#usage-based-billing-for-compute"
|
||||
passHref
|
||||
>
|
||||
<a target="_blank" className="transition text-brand hover:text-brand-600">
|
||||
usage-based billing for compute
|
||||
</a>
|
||||
</Link>{' '}
|
||||
or{' '}
|
||||
<Link href="https://supabase.com/docs/guides/platform/compute-add-ons" passHref>
|
||||
<a target="_blank" className="transition text-brand hover:text-brand-600">
|
||||
Compute Add-ons
|
||||
</a>
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -73,7 +95,7 @@ export default function ComputePricingModal({ showComputeModal, setShowComputeMo
|
||||
{i === 0 && (
|
||||
<tr className="">
|
||||
<td className="pb-1 bg-scale-700 px-3 py-1 -mr-1 border-l-4 border-scale-700">
|
||||
<span className="">Included in Free and Pro plan</span>
|
||||
<span className="">First instance is free on paid plans</span>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
|
||||
const DatabaseVisual = () => {
|
||||
const containerRef = useRef(null)
|
||||
const ref = useRef(null)
|
||||
const [isActive, setIsActive] = useState(false)
|
||||
const [gradientPos, setGradientPos] = useState({ x: 0, y: 0 })
|
||||
|
||||
const handleGlow = (event: any) => {
|
||||
@@ -22,7 +22,6 @@ const DatabaseVisual = () => {
|
||||
|
||||
const isContainerHovered =
|
||||
xCont > -3 && xCont < containerWidth + 3 && yCont > -3 && yCont < containerHeight + 3
|
||||
setIsActive(isContainerHovered)
|
||||
|
||||
if (!isContainerHovered) return
|
||||
|
||||
@@ -42,6 +41,8 @@ const DatabaseVisual = () => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const gradientTransform = `translate(${gradientPos?.x} ${gradientPos?.y}) rotate(45) scale(166 180)`
|
||||
|
||||
return (
|
||||
<figure
|
||||
className="absolute inset-0 z-0"
|
||||
@@ -52,300 +53,94 @@ const DatabaseVisual = () => {
|
||||
<span className="absolute w-full lg:w-auto h-full lg:aspect-square flex items-end lg:items-center justify-center lg:justify-end right-0 left-0 lg:left-auto top-24 md:top-24 lg:top-0 lg:bottom-0 my-auto">
|
||||
<svg
|
||||
ref={ref}
|
||||
width="390"
|
||||
height="430"
|
||||
viewBox="0 0 390 430"
|
||||
fill="none"
|
||||
className="scale-75 lg:scale-90"
|
||||
className="absolute w-full h-full z-10 m-auto"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_467_4905)">
|
||||
<g filter="url(#filter5_bd_467_4905)">
|
||||
<path
|
||||
d="M0.183357 -10.5469L0.183377 446.906M14.1834 -10.5469L14.1834 446.906M28.1834 -10.5469L28.1834 446.906M42.1834 -10.5469L42.1834 446.906M56.1834 -10.5469L56.1835 446.906M70.1835 -10.5469L70.1835 446.906M84.1835 -10.5469L84.1835 446.906M98.1835 -10.5469L98.1835 446.906M112.184 -10.5469L112.184 446.906M126.184 -10.5469L126.184 446.906M140.184 -10.5469L140.184 446.906M154.184 -10.5469L154.184 446.906M168.184 -10.5469L168.184 446.906M182.184 -10.5469L182.184 446.906M196.184 -10.5469L196.184 446.906M210.184 -10.5469L210.184 446.906M224.184 -10.5469L224.184 446.906M238.184 -10.5469L238.184 446.906M252.184 -10.5469L252.184 446.906M266.184 -10.5469L266.184 446.906M280.184 -10.5469L280.184 446.906M294.184 -10.5469L294.184 446.906M308.184 -10.5469L308.184 446.906M322.184 -10.5469L322.184 446.906M336.184 -10.5469L336.184 446.906M350.184 -10.5469L350.184 446.906M364.184 -10.5469L364.184 446.906M378.184 -10.5469L378.184 446.906M392.184 -10.5469L392.184 446.906M457.341 -0.25H-0.11253M457.341 13.75H-0.11253M457.341 27.75H-0.112531M457.341 41.7501H-0.112532M457.341 55.7501H-0.112532M457.341 69.7501H-0.112533M457.341 83.7501H-0.112534M457.341 97.7501H-0.112534M457.341 111.75H-0.112535M457.341 125.75H-0.112535M457.341 139.75H-0.112536M457.341 153.75H-0.112537M457.341 167.75H-0.112537M457.341 181.75H-0.112538M457.341 195.75H-0.112538M457.341 209.75H-0.112539M457.341 223.75H-0.11254M457.341 237.75H-0.11254M457.341 251.75H-0.112541M457.341 265.75H-0.112541M457.341 279.75H-0.112542M457.341 293.75H-0.112543M457.341 307.75H-0.112543M457.341 321.75H-0.112544M457.341 335.751H-0.112545M457.341 349.751H-0.112545M457.341 363.751H-0.112546M457.341 377.751H-0.112546M457.341 391.751H-0.112547M457.341 405.751H-0.112548M457.341 419.751H-0.112548M457.341 433.751H-0.112549"
|
||||
stroke="#282828"
|
||||
d="M192.144 125.816H138.679C130.173 125.816 122.52 130.986 119.345 138.877L99.0045 189.43C95.9432 197.038 97.6597 205.736 103.382 211.611L113.614 222.117C118.406 227.036 121.088 233.633 121.088 240.501L121.087 254.974C121.087 275.171 137.46 291.543 157.656 291.543C163.816 291.543 168.81 286.55 168.81 280.39L168.811 194.149C168.811 175.52 176.252 157.663 189.479 144.547C192.225 141.824 196.657 141.843 199.379 144.588C202.101 147.333 202.082 151.766 199.337 154.488C188.76 164.976 182.811 179.254 182.811 194.149L182.81 280.39C182.81 294.282 171.548 305.543 157.656 305.543C129.728 305.543 107.087 282.903 107.087 254.974L107.088 240.5C107.088 237.282 105.831 234.191 103.585 231.885L93.353 221.38C83.7626 211.533 80.8857 196.956 86.0164 184.204L106.357 133.651C111.665 120.459 124.458 111.816 138.679 111.816H194.408V111.9H204.747C253.851 111.9 293.657 151.706 293.657 200.81V251.652C293.657 255.518 290.523 258.652 286.657 258.652C282.791 258.652 279.657 255.518 279.657 251.652V200.81C279.657 159.438 246.119 125.9 204.747 125.9H193.23C192.86 125.9 192.498 125.871 192.144 125.816Z"
|
||||
stroke="#A0A0A0"
|
||||
strokeWidth="0.55"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<g filter="url(#filter0_f_467_4905)">
|
||||
<ellipse cx="0.312866" cy="214.736" rx="78.8492" ry="311.736" fill="#232323" />
|
||||
</g>
|
||||
<g filter="url(#filter1_f_467_4905)">
|
||||
<ellipse cx="390.849" cy="214.736" rx="78.8492" ry="311.736" fill="#232323" />
|
||||
</g>
|
||||
<g filter="url(#filter2_f_467_4905)">
|
||||
<ellipse
|
||||
cx="194.736"
|
||||
cy="-0.0142959"
|
||||
rx="78.8492"
|
||||
ry="311.736"
|
||||
transform="rotate(-90 194.736 -0.0142959)"
|
||||
fill="#232323"
|
||||
/>
|
||||
</g>
|
||||
<g filter="url(#filter3_f_467_4905)">
|
||||
<ellipse
|
||||
cx="194.736"
|
||||
cy="430.849"
|
||||
rx="78.8492"
|
||||
ry="311.736"
|
||||
transform="rotate(-90 194.736 430.849)"
|
||||
fill="#232323"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
opacity="0.2"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M69.7673 129.848C69.7673 111.898 84.3181 97.3477 102.267 97.3477H290.044C307.993 97.3477 322.544 111.898 322.544 129.848V157.218C322.55 157.466 322.552 157.714 322.552 157.963V289.787C322.552 290.036 322.55 290.284 322.544 290.532V317.727C322.544 335.676 307.993 350.227 290.044 350.227H102.267C84.3181 350.227 69.7673 335.676 69.7673 317.727V232.148C69.5834 229.361 69.49 226.549 69.49 223.716C69.49 220.882 69.5834 218.071 69.7673 215.284V129.848ZM70.7673 232.114V215.317C71.8986 198.244 76.4539 182.107 83.7527 167.586V279.845C76.4539 265.325 71.8986 249.188 70.7673 232.114ZM70.7673 241.736V289.787C70.7673 301.431 77.0854 311.6 86.4804 317.052C84.7263 313.056 83.7527 308.641 83.7527 303.998V282.038C77.2909 269.667 72.8206 256.091 70.7673 241.736ZM84.7527 283.914V303.998C84.7527 308.971 85.905 313.674 87.9571 317.856C91.0513 319.437 94.4415 320.521 98.0224 321.004C97.9003 319.858 97.8377 318.695 97.8377 317.518V303.399C92.8997 297.345 88.5126 290.825 84.7527 283.914ZM98.8377 304.609V317.518C98.8377 318.737 98.907 319.941 99.0419 321.124C100.102 321.232 101.178 321.287 102.267 321.287H115.675C109.555 316.266 103.916 310.68 98.8377 304.609ZM117.261 321.287C110.509 315.856 104.333 309.739 98.8377 303.042V144.39C104.226 137.823 110.269 131.814 116.867 126.463H274.408C281.345 132.088 287.668 138.441 293.262 145.405V302.026C287.562 309.122 281.105 315.583 274.014 321.287H117.261ZM116.91 322.287H102.267C101.224 322.287 100.193 322.238 99.1747 322.142C99.7118 325.793 100.875 329.24 102.552 332.371C106.695 334.375 111.342 335.498 116.253 335.498H137.119C129.948 331.736 123.184 327.305 116.91 322.287ZM139.305 335.498C131.928 331.773 124.97 327.34 118.521 322.287H272.754C266.305 327.34 259.347 331.773 251.97 335.498H139.305ZM139.066 336.498H116.253C111.688 336.498 107.344 335.557 103.402 333.859C108.928 342.947 118.924 349.018 130.338 349.018H180.952C166.042 347.289 151.926 342.961 139.066 336.498ZM210.323 349.018H261.762C273.109 349.018 283.055 343.018 288.6 334.017C284.758 335.616 280.544 336.498 276.124 336.498H252.208C239.349 342.961 225.233 347.289 210.323 349.018ZM249.945 336.498C233.518 344.422 215.097 348.863 195.637 348.863C176.178 348.863 157.757 344.422 141.33 336.498H249.945ZM254.156 335.498H276.124C280.887 335.498 285.403 334.441 289.451 332.548C291.177 329.376 292.373 325.875 292.921 322.162C291.976 322.245 291.019 322.287 290.052 322.287H274.365C268.091 327.305 261.326 331.736 254.156 335.498ZM275.6 321.287H290.052C291.065 321.287 292.067 321.239 293.055 321.146C293.191 319.955 293.262 318.745 293.262 317.518V303.614C287.971 310.07 282.053 315.992 275.6 321.287ZM294.262 302.378V317.518C294.262 318.705 294.198 319.878 294.074 321.033C297.766 320.562 301.259 319.452 304.438 317.818C306.479 313.645 307.624 308.955 307.624 303.998V281.843C303.832 289.134 299.349 296.008 294.262 302.378ZM308.624 279.879V303.998C308.624 308.624 307.657 313.024 305.915 317.007C315.071 311.66 321.286 301.826 321.544 290.52V231.574C320.483 248.846 315.946 265.178 308.624 279.879ZM321.544 215.857V157.23C321.286 145.932 315.08 136.104 305.935 130.754C307.665 134.726 308.624 139.11 308.624 143.719V167.553C315.946 182.254 320.483 198.586 321.544 215.857ZM308.624 169.834C316.42 186.153 320.785 204.424 320.785 223.716C320.785 243.007 316.42 261.279 308.624 277.598V169.834ZM307.624 167.789V279.643C303.87 287.144 299.385 294.216 294.262 300.765V146.667C299.385 153.215 303.87 160.287 307.624 167.789ZM307.624 165.588V143.719C307.624 138.779 306.487 134.105 304.46 129.943C301.283 128.307 297.792 127.194 294.101 126.721C294.207 127.79 294.262 128.875 294.262 129.973V145.054C299.349 151.424 303.832 158.298 307.624 165.588ZM293.262 143.818V129.973C293.262 128.836 293.201 127.713 293.084 126.607C292.086 126.512 291.075 126.463 290.052 126.463H275.986C282.289 131.676 288.077 137.49 293.262 143.818ZM274.762 125.463H290.052C291.032 125.463 292.002 125.506 292.959 125.591C292.445 121.899 291.292 118.412 289.614 115.245C285.525 113.305 280.951 112.219 276.124 112.219H254.696C261.817 115.999 268.533 120.441 274.762 125.463ZM252.772 111.219H276.124C280.615 111.219 284.894 112.13 288.785 113.777C283.278 104.608 273.236 98.4727 261.762 98.4727H210.82C225.764 100.265 239.905 104.671 252.772 111.219ZM180.455 98.4727H130.338C118.796 98.4727 108.704 104.68 103.218 113.938C107.208 112.189 111.617 111.219 116.253 111.219H138.503C151.37 104.671 165.511 100.265 180.455 98.4727ZM140.743 111.219C157.317 103.116 175.946 98.5684 195.637 98.5684C215.329 98.5684 233.958 103.116 250.532 111.219H140.743ZM138.743 112.219H252.532C259.854 115.963 266.759 120.407 273.159 125.463H118.116C124.516 120.407 131.421 115.963 138.743 112.219ZM136.579 112.219H116.253C111.279 112.219 106.574 113.372 102.391 115.425C100.761 118.55 99.6398 121.982 99.1371 125.612C100.167 125.513 101.211 125.463 102.267 125.463H116.513C122.742 120.441 129.458 115.999 136.579 112.219ZM115.289 126.463H102.267C101.169 126.463 100.083 126.519 99.013 126.629C98.8971 127.728 98.8377 128.843 98Line truncated
|
||||
fill="#A0A0A0"
|
||||
d="M210.03 283.94C210.03 280.074 206.896 276.94 203.03 276.94C199.164 276.94 196.03 280.074 196.03 283.94V287.053C196.03 314.012 217.884 335.867 244.843 335.867C271.194 335.867 292.668 314.988 293.624 288.871H318.238C322.104 288.871 325.238 285.737 325.238 281.871C325.238 278.005 322.104 274.871 318.238 274.871H291.397C260.653 274.871 231.141 262.788 209.224 241.228C206.468 238.517 202.036 238.553 199.325 241.309C196.614 244.065 196.65 248.497 199.406 251.209C221.131 272.58 249.522 285.632 279.634 288.343C278.955 306.972 263.639 321.867 244.843 321.867C225.616 321.867 210.03 306.28 210.03 287.053V283.94Z"
|
||||
stroke="#A0A0A0"
|
||||
strokeWidth="0.55"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<path
|
||||
d="M238.03 202.145C238.03 206.937 241.915 210.822 246.707 210.822C251.499 210.822 255.383 206.937 255.383 202.145C255.383 197.353 251.499 193.469 246.707 193.469C241.915 193.469 238.03 197.353 238.03 202.145Z"
|
||||
stroke="#A0A0A0"
|
||||
strokeWidth="0.55"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
</g>
|
||||
<g className="opacity-0 transition-opacity group-hover:opacity-100">
|
||||
<path
|
||||
d="M192.144 125.816H138.679C130.173 125.816 122.52 130.986 119.345 138.877L99.0045 189.43C95.9432 197.038 97.6597 205.736 103.382 211.611L113.614 222.117C118.406 227.036 121.088 233.633 121.088 240.501L121.087 254.974C121.087 275.171 137.46 291.543 157.656 291.543C163.816 291.543 168.81 286.55 168.81 280.39L168.811 194.149C168.811 175.52 176.252 157.663 189.479 144.547C192.225 141.824 196.657 141.843 199.379 144.588C202.101 147.333 202.082 151.766 199.337 154.488C188.76 164.976 182.811 179.254 182.811 194.149L182.81 280.39C182.81 294.282 171.548 305.543 157.656 305.543C129.728 305.543 107.087 282.903 107.087 254.974L107.088 240.5C107.088 237.282 105.831 234.191 103.585 231.885L93.353 221.38C83.7626 211.533 80.8857 196.956 86.0164 184.204L106.357 133.651C111.665 120.459 124.458 111.816 138.679 111.816H194.408V111.9H204.747C253.851 111.9 293.657 151.706 293.657 200.81V251.652C293.657 255.518 290.523 258.652 286.657 258.652C282.791 258.652 279.657 255.518 279.657 251.652V200.81C279.657 159.438 246.119 125.9 204.747 125.9H193.23C192.86 125.9 192.498 125.871 192.144 125.816Z"
|
||||
stroke="url(#paint4_radial_467_4905)"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<path
|
||||
d="M210.03 283.94C210.03 280.074 206.896 276.94 203.03 276.94C199.164 276.94 196.03 280.074 196.03 283.94V287.053C196.03 314.012 217.884 335.867 244.843 335.867C271.194 335.867 292.668 314.988 293.624 288.871H318.238C322.104 288.871 325.238 285.737 325.238 281.871C325.238 278.005 322.104 274.871 318.238 274.871H291.397C260.653 274.871 231.141 262.788 209.224 241.228C206.468 238.517 202.036 238.553 199.325 241.309C196.614 244.065 196.65 248.497 199.406 251.209C221.131 272.58 249.522 285.632 279.634 288.343C278.955 306.972 263.639 321.867 244.843 321.867C225.616 321.867 210.03 306.28 210.03 287.053V283.94Z"
|
||||
stroke="url(#paint5_radial_467_4905)"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<path
|
||||
d="M238.03 202.145C238.03 206.937 241.915 210.822 246.707 210.822C251.499 210.822 255.383 206.937 255.383 202.145C255.383 197.353 251.499 193.469 246.707 193.469C241.915 193.469 238.03 197.353 238.03 202.145Z"
|
||||
stroke="url(#paint6_radial_467_4905)"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<g filter="url(#filter5_bd_467_4905)">
|
||||
<path
|
||||
d="M192.144 125.816H138.679C130.173 125.816 122.52 130.986 119.345 138.877L99.0045 189.43C95.9432 197.038 97.6597 205.736 103.382 211.611L113.614 222.117C118.406 227.036 121.088 233.633 121.088 240.501L121.087 254.974C121.087 275.171 137.46 291.543 157.656 291.543C163.816 291.543 168.81 286.55 168.81 280.39L168.811 194.149C168.811 175.52 176.252 157.663 189.479 144.547C192.225 141.824 196.657 141.843 199.379 144.588C202.101 147.333 202.082 151.766 199.337 154.488C188.76 164.976 182.811 179.254 182.811 194.149L182.81 280.39C182.81 294.282 171.548 305.543 157.656 305.543C129.728 305.543 107.087 282.903 107.087 254.974L107.088 240.5C107.088 237.282 105.831 234.191 103.585 231.885L93.353 221.38C83.7626 211.533 80.8857 196.956 86.0164 184.204L106.357 133.651C111.665 120.459 124.458 111.816 138.679 111.816H194.408V111.9H204.747C253.851 111.9 293.657 151.706 293.657 200.81V251.652C293.657 255.518 290.523 258.652 286.657 258.652C282.791 258.652 279.657 255.518 279.657 251.652V200.81C279.657 159.438 246.119 125.9 204.747 125.9H193.23C192.86 125.9 192.498 125.871 192.144 125.816Z"
|
||||
fill="url(#paint1_linear_467_4905)"
|
||||
fillOpacity="0.07"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<path
|
||||
d="M210.03 283.94C210.03 280.074 206.896 276.94 203.03 276.94C199.164 276.94 196.03 280.074 196.03 283.94V287.053C196.03 314.012 217.884 335.867 244.843 335.867C271.194 335.867 292.668 314.988 293.624 288.871H318.238C322.104 288.871 325.238 285.737 325.238 281.871C325.238 278.005 322.104 274.871 318.238 274.871H291.397C260.653 274.871 231.141 262.788 209.224 241.228C206.468 238.517 202.036 238.553 199.325 241.309C196.614 244.065 196.65 248.497 199.406 251.209C221.131 272.58 249.522 285.632 279.634 288.343C278.955 306.972 263.639 321.867 244.843 321.867C225.616 321.867 210.03 306.28 210.03 287.053V283.94Z"
|
||||
fill="url(#paint2_linear_467_4905)"
|
||||
fillOpacity="0.07"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<path
|
||||
d="M238.03 202.145C238.03 206.937 241.915 210.822 246.707 210.822C251.499 210.822 255.383 206.937 255.383 202.145C255.383 197.353 251.499 193.469 246.707 193.469C241.915 193.469 238.03 197.353 238.03 202.145Z"
|
||||
fill="url(#paint3_linear_467_4905)"
|
||||
fillOpacity="0.07"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<path
|
||||
d="M192.144 125.816H138.679C130.173 125.816 122.52 130.986 119.345 138.877L99.0045 189.43C95.9432 197.038 97.6597 205.736 103.382 211.611L113.614 222.117C118.406 227.036 121.088 233.633 121.088 240.501L121.087 254.974C121.087 275.171 137.46 291.543 157.656 291.543C163.816 291.543 168.81 286.55 168.81 280.39L168.811 194.149C168.811 175.52 176.252 157.663 189.479 144.547C192.225 141.824 196.657 141.843 199.379 144.588C202.101 147.333 202.082 151.766 199.337 154.488C188.76 164.976 182.811 179.254 182.811 194.149L182.81 280.39C182.81 294.282 171.548 305.543 157.656 305.543C129.728 305.543 107.087 282.903 107.087 254.974L107.088 240.5C107.088 237.282 105.831 234.191 103.585 231.885L93.353 221.38C83.7626 211.533 80.8857 196.956 86.0164 184.204L106.357 133.651C111.665 120.459 124.458 111.816 138.679 111.816H194.408V111.9H204.747C253.851 111.9 293.657 151.706 293.657 200.81V251.652C293.657 255.518 290.523 258.652 286.657 258.652C282.791 258.652 279.657 255.518 279.657 251.652V200.81C279.657 159.438 246.119 125.9 204.747 125.9H193.23C192.86 125.9 192.498 125.871 192.144 125.816Z"
|
||||
stroke="#A0A0A0"
|
||||
strokeWidth="0.5"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<path
|
||||
d="M210.03 283.94C210.03 280.074 206.896 276.94 203.03 276.94C199.164 276.94 196.03 280.074 196.03 283.94V287.053C196.03 314.012 217.884 335.867 244.843 335.867C271.194 335.867 292.668 314.988 293.624 288.871H318.238C322.104 288.871 325.238 285.737 325.238 281.871C325.238 278.005 322.104 274.871 318.238 274.871H291.397C260.653 274.871 231.141 262.788 209.224 241.228C206.468 238.517 202.036 238.553 199.325 241.309C196.614 244.065 196.65 248.497 199.406 251.209C221.131 272.58 249.522 285.632 279.634 288.343C278.955 306.972 263.639 321.867 244.843 321.867C225.616 321.867 210.03 306.28 210.03 287.053V283.94Z"
|
||||
stroke="#A0A0A0"
|
||||
strokeWidth="0.5"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<path
|
||||
d="M238.03 202.145C238.03 206.937 241.915 210.822 246.707 210.822C251.499 210.822 255.383 206.937 255.383 202.145C255.383 197.353 251.499 193.469 246.707 193.469C241.915 193.469 238.03 197.353 238.03 202.145Z"
|
||||
stroke="#A0A0A0"
|
||||
strokeWidth="0.5"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M192.144 125.816H138.679C130.173 125.816 122.52 130.986 119.345 138.877L99.0045 189.43C95.9432 197.038 97.6597 205.736 103.382 211.611L113.614 222.117C118.406 227.036 121.088 233.633 121.088 240.501L121.087 254.974C121.087 275.171 137.46 291.543 157.656 291.543C163.816 291.543 168.81 286.55 168.81 280.39L168.811 194.149C168.811 175.52 176.252 157.663 189.479 144.547C192.225 141.824 196.657 141.843 199.379 144.588C202.101 147.333 202.082 151.766 199.337 154.488C188.76 164.976 182.811 179.254 182.811 194.149L182.81 280.39C182.81 294.282 171.548 305.543 157.656 305.543C129.728 305.543 107.087 282.903 107.087 254.974L107.088 240.5C107.088 237.282 105.831 234.191 103.585 231.885L93.353 221.38C83.7626 211.533 80.8857 196.956 86.0164 184.204L106.357 133.651C111.665 120.459 124.458 111.816 138.679 111.816H194.408V111.9H204.747C253.851 111.9 293.657 151.706 293.657 200.81V251.652C293.657 255.518 290.523 258.652 286.657 258.652C282.791 258.652 279.657 255.518 279.657 251.652V200.81C279.657 159.438 246.119 125.9 204.747 125.9H193.23C192.86 125.9 192.498 125.871 192.144 125.816Z"
|
||||
stroke="url(#paint4_radial_467_4905)"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<path
|
||||
d="M210.03 283.94C210.03 280.074 206.896 276.94 203.03 276.94C199.164 276.94 196.03 280.074 196.03 283.94V287.053C196.03 314.012 217.884 335.867 244.843 335.867C271.194 335.867 292.668 314.988 293.624 288.871H318.238C322.104 288.871 325.238 285.737 325.238 281.871C325.238 278.005 322.104 274.871 318.238 274.871H291.397C260.653 274.871 231.141 262.788 209.224 241.228C206.468 238.517 202.036 238.553 199.325 241.309C196.614 244.065 196.65 248.497 199.406 251.209C221.131 272.58 249.522 285.632 279.634 288.343C278.955 306.972 263.639 321.867 244.843 321.867C225.616 321.867 210.03 306.28 210.03 287.053V283.94Z"
|
||||
stroke="url(#paint5_radial_467_4905)"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
<path
|
||||
d="M238.03 202.145C238.03 206.937 241.915 210.822 246.707 210.822C251.499 210.822 255.383 206.937 255.383 202.145C255.383 197.353 251.499 193.469 246.707 193.469C241.915 193.469 238.03 197.353 238.03 202.145Z"
|
||||
stroke="url(#paint6_radial_467_4905)"
|
||||
shapeRendering="crispEdges"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_f_467_4905"
|
||||
x="-194.536"
|
||||
y="-213"
|
||||
width="389.698"
|
||||
height="855.472"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur stdDeviation="58" result="effect1_foregroundBlur_467_4905" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter1_f_467_4905"
|
||||
x="196"
|
||||
y="-213"
|
||||
width="389.698"
|
||||
height="855.472"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur stdDeviation="58" result="effect1_foregroundBlur_467_4905" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter2_f_467_4905"
|
||||
x="-233"
|
||||
y="-194.864"
|
||||
width="855.472"
|
||||
height="389.699"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur stdDeviation="58" result="effect1_foregroundBlur_467_4905" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter3_f_467_4905"
|
||||
x="-233"
|
||||
y="236"
|
||||
width="855.472"
|
||||
height="389.699"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur stdDeviation="58" result="effect1_foregroundBlur_467_4905" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter4_f_467_4905"
|
||||
x="161.205"
|
||||
y="-19.729"
|
||||
width="273.378"
|
||||
height="265.128"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feGaussianBlur stdDeviation="25" result="effect1_foregroundBlur_467_4905" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter5_bd_467_4905"
|
||||
x="51.2566"
|
||||
y="79.5664"
|
||||
width="306.232"
|
||||
height="288.55"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feGaussianBlur in="BackgroundImageFix" stdDeviation="1" />
|
||||
<feComposite
|
||||
in2="SourceAlpha"
|
||||
operator="in"
|
||||
result="effect1_backgroundBlur_467_4905"
|
||||
/>
|
||||
<feColorMatrix
|
||||
in="SourceAlpha"
|
||||
type="matrix"
|
||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||
result="hardAlpha"
|
||||
/>
|
||||
<feOffset />
|
||||
<feGaussianBlur stdDeviation="16" />
|
||||
<feComposite in2="hardAlpha" operator="out" />
|
||||
<feColorMatrix
|
||||
type="matrix"
|
||||
values="0 0 0 0 0.0313726 0 0 0 0 0.0470588 0 0 0 0 0.0470588 0 0 0 0.5 0"
|
||||
/>
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in2="effect1_backgroundBlur_467_4905"
|
||||
result="effect2_dropShadow_467_4905"
|
||||
/>
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="effect2_dropShadow_467_4905"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
<radialGradient
|
||||
id="paint0_radial_467_4905"
|
||||
id="paint4_radial_467_4905"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(97.8217 68.9936) rotate(90) scale(68.9936 97.8217)"
|
||||
gradientTransform={gradientTransform}
|
||||
>
|
||||
<stop stopColor="#85E0B7" />
|
||||
<stop offset="1" stopColor="#85E0B7" stopOpacity="0" />
|
||||
<stop stopColor="var(--colors-brand9)" />
|
||||
<stop offset="1" stopColor="#7C7C7C" />
|
||||
</radialGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_467_4905"
|
||||
x1="146.817"
|
||||
y1="128.841"
|
||||
x2="146.817"
|
||||
y2="292.676"
|
||||
<radialGradient
|
||||
id="paint5_radial_467_4905"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform={gradientTransform}
|
||||
>
|
||||
<stop stopColor="white" />
|
||||
<stop stopColor="var(--colors-brand9)" />
|
||||
<stop offset="1" stopColor="#7C7C7C" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint2_linear_467_4905"
|
||||
x1="146.817"
|
||||
y1="128.841"
|
||||
x2="146.817"
|
||||
y2="292.676"
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id="paint6_radial_467_4905"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform={gradientTransform}
|
||||
>
|
||||
<stop stopColor="white" />
|
||||
<stop stopColor="var(--colors-brand9)" />
|
||||
<stop offset="1" stopColor="#7C7C7C" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint3_linear_467_4905"
|
||||
x1="146.817"
|
||||
y1="128.841"
|
||||
x2="146.817"
|
||||
y2="292.676"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="white" />
|
||||
<stop offset="1" stopColor="#7C7C7C" />
|
||||
</linearGradient>
|
||||
{isActive && (
|
||||
<>
|
||||
<radialGradient
|
||||
id="paint4_radial_467_4905"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform={`translate(${gradientPos?.x} ${gradientPos.y}) rotate(45) scale(166 180)`}
|
||||
>
|
||||
<stop stopColor="#3FCF8E" />
|
||||
<stop offset="1" stopColor="#7C7C7C" />
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id="paint5_radial_467_4905"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform={`translate(${gradientPos?.x} ${gradientPos.y}) rotate(45) scale(166 180)`}
|
||||
>
|
||||
<stop stopColor="#3FCF8E" />
|
||||
<stop offset="1" stopColor="#7C7C7C" />{' '}
|
||||
</radialGradient>
|
||||
<radialGradient
|
||||
id="paint6_radial_467_4905"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform={`translate(${gradientPos?.x} ${gradientPos.y}) rotate(45) scale(166 180)`}
|
||||
>
|
||||
<stop stopColor="#3FCF8E" />
|
||||
<stop offset="1" stopColor="#7C7C7C" />
|
||||
</radialGradient>
|
||||
</>
|
||||
)}
|
||||
<clipPath id="clip0_467_4905">
|
||||
<rect width="390" height="430" fill="white" />
|
||||
</clipPath>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<Image
|
||||
src={`/images/index/products/database.svg`}
|
||||
alt="Supabase Postgres database"
|
||||
layout="fill"
|
||||
objectFit="contain"
|
||||
objectPosition="center"
|
||||
className="absolute inset-0 z-0 w-full lg:w-auto h-full transition-opacity group-hover:opacity-80"
|
||||
quality={100}
|
||||
/>
|
||||
</span>
|
||||
</figure>
|
||||
)
|
||||
|
||||
@@ -32,7 +32,7 @@ const FunctionsVisual = () => {
|
||||
viewBox="0 0 289 430"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
className="absolute w-full aspect-[1/1.44] inset-0 z-10 m-auto"
|
||||
className="absolute w-[calc(100%+8px)] aspect-[1/1.44] inset-y-0 -inset-x-2 z-10 m-auto"
|
||||
>
|
||||
<g clipPath="url(#clip0_444_4697)">
|
||||
<g filter="url(#filter0_f_444_4697)">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import Panel from '~/components/Panel'
|
||||
import { detectBrowser } from 'common'
|
||||
|
||||
const ProductCard = ({
|
||||
className,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
{
|
||||
"columns": [
|
||||
{ "key": "plan", "title": "Plan", "value": "Starter" },
|
||||
{ "key": "pricing", "title": "Pricing", "value": "$0" },
|
||||
{ "key": "pricing", "title": "Price USD", "value": "$10" },
|
||||
{ "key": "cpu", "title": "CPU", "value": "2-core ARM" },
|
||||
{ "key": "dedicated", "title": "Dedicated", "value": false },
|
||||
{ "key": "memory", "title": "Memory", "value": "1 GB" },
|
||||
@@ -36,7 +36,7 @@
|
||||
{
|
||||
"columns": [
|
||||
{ "key": "plan", "title": "Plan", "value": "Small" },
|
||||
{ "key": "pricing", "title": "Pricing", "value": "$5" },
|
||||
{ "key": "pricing", "title": "Price USD", "value": "$15" },
|
||||
{ "key": "cpu", "title": "CPU", "value": "2-core ARM" },
|
||||
{ "key": "dedicated", "title": "Dedicated", "value": false },
|
||||
{ "key": "memory", "title": "Memory", "value": "2 GB" },
|
||||
@@ -61,7 +61,7 @@
|
||||
{
|
||||
"columns": [
|
||||
{ "key": "plan", "title": "Plan", "value": "Medium" },
|
||||
{ "key": "pricing", "title": "Pricing", "value": "$50" },
|
||||
{ "key": "pricing", "title": "Price USD", "value": "$60" },
|
||||
{ "key": "cpu", "title": "CPU", "value": "2-core ARM" },
|
||||
{ "key": "dedicated", "title": "Dedicated", "value": false },
|
||||
{ "key": "memory", "title": "Memory", "value": "4 GB" },
|
||||
@@ -86,7 +86,7 @@
|
||||
{
|
||||
"columns": [
|
||||
{ "key": "plan", "title": "Plan", "value": "Large" },
|
||||
{ "key": "pricing", "title": "Pricing", "value": "$100" },
|
||||
{ "key": "pricing", "title": "Price USD", "value": "$110" },
|
||||
{ "key": "cpu", "title": "CPU", "value": "2-core ARM" },
|
||||
{ "key": "dedicated", "title": "Dedicated", "value": true },
|
||||
{ "key": "memory", "title": "Memory", "value": "8 GB" },
|
||||
@@ -111,7 +111,7 @@
|
||||
{
|
||||
"columns": [
|
||||
{ "key": "plan", "title": "Plan", "value": "XL" },
|
||||
{ "key": "pricing", "title": "Pricing", "value": "$200" },
|
||||
{ "key": "pricing", "title": "Price USD", "value": "$210" },
|
||||
{ "key": "cpu", "title": "CPU", "value": "4-core ARM" },
|
||||
{ "key": "dedicated", "title": "Dedicated", "value": true },
|
||||
{ "key": "memory", "title": "Memory", "value": "16 GB" },
|
||||
@@ -136,7 +136,7 @@
|
||||
{
|
||||
"columns": [
|
||||
{ "key": "plan", "title": "Plan", "value": "2XL" },
|
||||
{ "key": "pricing", "title": "Pricing", "value": "$400" },
|
||||
{ "key": "pricing", "title": "Price USD", "value": "$410" },
|
||||
{ "key": "cpu", "title": "CPU", "value": "8-core ARM" },
|
||||
{ "key": "dedicated", "title": "Dedicated", "value": true },
|
||||
{ "key": "memory", "title": "Memory", "value": "32 GB" },
|
||||
@@ -161,7 +161,7 @@
|
||||
{
|
||||
"columns": [
|
||||
{ "key": "plan", "title": "Plan", "value": "4XL" },
|
||||
{ "key": "pricing", "title": "Pricing", "value": "$950" },
|
||||
{ "key": "pricing", "title": "Price USD", "value": "$960" },
|
||||
{ "key": "cpu", "title": "CPU", "value": "16-core ARM" },
|
||||
{ "key": "dedicated", "title": "Dedicated", "value": true },
|
||||
{ "key": "memory", "title": "Memory", "value": "64 GB" },
|
||||
@@ -186,7 +186,7 @@
|
||||
{
|
||||
"columns": [
|
||||
{ "key": "plan", "title": "Plan", "value": "8XL" },
|
||||
{ "key": "pricing", "title": "Pricing", "value": "$1,860" },
|
||||
{ "key": "pricing", "title": "Price USD", "value": "$1,870" },
|
||||
{ "key": "cpu", "title": "CPU", "value": "32-core ARM" },
|
||||
{ "key": "dedicated", "title": "Dedicated", "value": true },
|
||||
{ "key": "memory", "title": "Memory", "value": "128 GB" },
|
||||
@@ -211,7 +211,7 @@
|
||||
{
|
||||
"columns": [
|
||||
{ "key": "plan", "title": "Plan", "value": "12XL" },
|
||||
{ "key": "pricing", "title": "Pricing", "value": "$2,790" },
|
||||
{ "key": "pricing", "title": "Price USD", "value": "$2,800" },
|
||||
{ "key": "cpu", "title": "CPU", "value": "48-core ARM" },
|
||||
{ "key": "dedicated", "title": "Dedicated", "value": true },
|
||||
{ "key": "memory", "title": "Memory", "value": "192 GB" },
|
||||
@@ -240,7 +240,7 @@
|
||||
{
|
||||
"columns": [
|
||||
{ "key": "plan", "title": "Plan", "value": "16XL" },
|
||||
{ "key": "pricing", "title": "Pricing", "value": "$3,720" },
|
||||
{ "key": "pricing", "title": "Price USD", "value": "$3,730" },
|
||||
{ "key": "cpu", "title": "CPU", "value": "64-core ARM" },
|
||||
{ "key": "dedicated", "title": "Dedicated", "value": true },
|
||||
{ "key": "memory", "title": "Memory", "value": "256 GB" },
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
CollapsibleTrigger_Shadcn_,
|
||||
CollapsibleContent_Shadcn_,
|
||||
IconTriangle,
|
||||
cn,
|
||||
} from 'ui'
|
||||
import ImageFadeStack from '~/components/ImageFadeStack'
|
||||
import ZoomableImg from '~/components/ZoomableImg/ZoomableImg'
|
||||
@@ -91,8 +92,8 @@ export default function mdxComponents(type?: 'blog' | 'lp' | undefined) {
|
||||
}
|
||||
return <img {...props} />
|
||||
},
|
||||
Img: ({ zoomable = true, ...props }: any) => (
|
||||
<figure className="m-0">
|
||||
Img: ({ zoomable = true, className, ...props }: any) => (
|
||||
<figure className={cn('m-0', className)}>
|
||||
<ZoomableImg zoomable={zoomable}>
|
||||
<span
|
||||
className={[
|
||||
|
||||
@@ -2288,4 +2288,9 @@ module.exports = [
|
||||
source: '/docs/guides/database/managing-passwords',
|
||||
destination: '/docs/guides/database/postgres/roles#passwords',
|
||||
},
|
||||
{
|
||||
permanent: true,
|
||||
source: '/blog/pgvector-v0-5-0-hnsw',
|
||||
destination: '/blog',
|
||||
},
|
||||
]
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Accordion, Button, IconCheck, Select } from 'ui'
|
||||
import { Accordion, Badge, Button, IconCheck, Select } from 'ui'
|
||||
import Solutions from 'data/Solutions'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import Link from 'next/link'
|
||||
@@ -13,6 +13,7 @@ import pricingFaq from '~/data/PricingFAQ.json'
|
||||
import { useTheme } from 'common/Providers'
|
||||
import ComputePricingModal from '~/components/Pricing/ComputePricingModal'
|
||||
import { plans } from 'shared-data/plans'
|
||||
import { ArrowNarrowRightIcon } from '@heroicons/react/outline'
|
||||
|
||||
export default function IndexPage() {
|
||||
const router = useRouter()
|
||||
@@ -121,7 +122,7 @@ export default function IndexPage() {
|
||||
<p className="p">{priceDescription}</p>
|
||||
</div>
|
||||
<p className="p">{description}</p>
|
||||
<Link href="https://supabase.com/dashboard" passHref>
|
||||
<Link href="https://supabase.com/dashboard/new" passHref>
|
||||
<a>
|
||||
<Button size="medium" block>
|
||||
Get started
|
||||
@@ -158,6 +159,46 @@ export default function IndexPage() {
|
||||
<p className="p text-lg">
|
||||
Start building for free, collaborate with a team, then scale to millions of users.
|
||||
</p>
|
||||
<div className="w-full flex justify-center items-center opacity-0 !animate-[fadeIn_0.5s_cubic-bezier(0.25,0.25,0,1)_0.5s_both]">
|
||||
<Link href="/blog/organization-based-billing" passHref>
|
||||
<a
|
||||
target="_blank"
|
||||
className="
|
||||
group
|
||||
relative
|
||||
flex flex-row
|
||||
items-center
|
||||
pr-3 p-1
|
||||
text-sm
|
||||
w-auto
|
||||
gap-2
|
||||
text-left
|
||||
rounded-full
|
||||
bg-opacity-20
|
||||
border
|
||||
border-background-surface-100
|
||||
hover:border-background-surface-300
|
||||
overflow-hidden
|
||||
focus:outline-none focus:ring-brand-600 focus:ring-2 focus:rounded-full
|
||||
"
|
||||
>
|
||||
<Badge color="brand" size="large" className="py-1">
|
||||
Update
|
||||
</Badge>
|
||||
<span className="text-foreground">Changes to how we bill</span>
|
||||
<ArrowNarrowRightIcon className="h-4 ml-2 -translate-x-1 transition-transform group-hover:translate-x-0" />
|
||||
<div
|
||||
className="absolute inset-0 -z-10 bg-gradient-to-br
|
||||
opacity-70
|
||||
overflow-hidden rounded-full
|
||||
from-background-surface-100
|
||||
to-background-surface-300
|
||||
backdrop-blur-md
|
||||
"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -404,7 +445,7 @@ export default function IndexPage() {
|
||||
<p className="mt-3 prose lg:max-w-lg">
|
||||
The Pro plan has a usage quota included and a spend cap turned on by default. If you
|
||||
need to go beyond the inclusive limits, simply switch off your spend cap to pay for
|
||||
additional usage and scale seamlessly. Note that your project will run into
|
||||
additional usage and scale seamlessly. Note that your projects will run into
|
||||
restrictions if you have the spend cap enabled and exhaust your quota.
|
||||
</p>
|
||||
</div>
|
||||
@@ -456,7 +497,7 @@ export default function IndexPage() {
|
||||
<MobileHeader
|
||||
plan="Free"
|
||||
price={'0'}
|
||||
priceDescription={'/mo'}
|
||||
priceDescription={'/month'}
|
||||
description={'Perfect for hobby projects and experiments'}
|
||||
/>
|
||||
<PricingTableRowMobile
|
||||
@@ -516,7 +557,7 @@ export default function IndexPage() {
|
||||
plan="Pro"
|
||||
from={false}
|
||||
price={'25'}
|
||||
priceDescription={'/mo + additional use'}
|
||||
priceDescription={'/month + additional use'}
|
||||
description={'Everything you need to scale your project into production'}
|
||||
/>
|
||||
<PricingTableRowMobile
|
||||
@@ -568,7 +609,7 @@ export default function IndexPage() {
|
||||
plan="Team"
|
||||
from={false}
|
||||
price={'599'}
|
||||
priceDescription={'/mo + additional use'}
|
||||
priceDescription={'/month + additional use'}
|
||||
description={'Collaborate with different permissions and access patterns'}
|
||||
/>
|
||||
<PricingTableRowMobile
|
||||
@@ -744,10 +785,10 @@ export default function IndexPage() {
|
||||
{plan.priceMonthly}
|
||||
</span>
|
||||
{['Pro', 'Free'].includes(plan.name) && (
|
||||
<p className="p text-[13px] leading-4 mt-1">per month</p>
|
||||
<p className="p text-[13px] leading-4 mt-1">/ month / org</p>
|
||||
)}
|
||||
{['Team'].includes(plan.name) && (
|
||||
<p className="p text-[13px] leading-4 mt-1">per month</p>
|
||||
<p className="p text-[13px] leading-4 mt-1">/ month / org</p>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -827,8 +868,8 @@ export default function IndexPage() {
|
||||
|
||||
<td className="px-6 pt-5">
|
||||
<Link
|
||||
href="https://supabase.com/dashboard"
|
||||
as="https://supabase.com/dashboard"
|
||||
href="https://supabase.com/dashboard/new?plan=free"
|
||||
as="https://supabase.com/dashboard/new?plan=free"
|
||||
>
|
||||
<a>
|
||||
<Button size="tiny" type="primary" block>
|
||||
@@ -840,8 +881,8 @@ export default function IndexPage() {
|
||||
|
||||
<td className="px-6 pt-5">
|
||||
<Link
|
||||
href="https://supabase.com/dashboard"
|
||||
as="https://supabase.com/dashboard"
|
||||
href="https://supabase.com/dashboard/new?plan=pro"
|
||||
as="https://supabase.com/dashboard/new?plan=pro"
|
||||
>
|
||||
<a>
|
||||
<Button size="tiny" type="primary" block>
|
||||
@@ -852,10 +893,10 @@ export default function IndexPage() {
|
||||
</td>
|
||||
|
||||
<td className="px-6 pt-5">
|
||||
<Link href="https://forms.supabase.com/team">
|
||||
<Link href="https://supabase.com/dashboard/new?plan=team">
|
||||
<a>
|
||||
<Button size="tiny" type="primary" block>
|
||||
Contact us
|
||||
Get Started
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 152 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 167 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 2.0 MiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 36 KiB |
@@ -0,0 +1,75 @@
|
||||
<svg width="390" height="430" viewBox="0 0 390 430" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_467_4905)">
|
||||
<path d="M0.183357 -10.5469L0.183377 446.906M14.1834 -10.5469L14.1834 446.906M28.1834 -10.5469L28.1834 446.906M42.1834 -10.5469L42.1834 446.906M56.1834 -10.5469L56.1835 446.906M70.1835 -10.5469L70.1835 446.906M84.1835 -10.5469L84.1835 446.906M98.1835 -10.5469L98.1835 446.906M112.184 -10.5469L112.184 446.906M126.184 -10.5469L126.184 446.906M140.184 -10.5469L140.184 446.906M154.184 -10.5469L154.184 446.906M168.184 -10.5469L168.184 446.906M182.184 -10.5469L182.184 446.906M196.184 -10.5469L196.184 446.906M210.184 -10.5469L210.184 446.906M224.184 -10.5469L224.184 446.906M238.184 -10.5469L238.184 446.906M252.184 -10.5469L252.184 446.906M266.184 -10.5469L266.184 446.906M280.184 -10.5469L280.184 446.906M294.184 -10.5469L294.184 446.906M308.184 -10.5469L308.184 446.906M322.184 -10.5469L322.184 446.906M336.184 -10.5469L336.184 446.906M350.184 -10.5469L350.184 446.906M364.184 -10.5469L364.184 446.906M378.184 -10.5469L378.184 446.906M392.184 -10.5469L392.184 446.906M457.341 -0.25H-0.11253M457.341 13.75H-0.11253M457.341 27.75H-0.112531M457.341 41.7501H-0.112532M457.341 55.7501H-0.112532M457.341 69.7501H-0.112533M457.341 83.7501H-0.112534M457.341 97.7501H-0.112534M457.341 111.75H-0.112535M457.341 125.75H-0.112535M457.341 139.75H-0.112536M457.341 153.75H-0.112537M457.341 167.75H-0.112537M457.341 181.75H-0.112538M457.341 195.75H-0.112538M457.341 209.75H-0.112539M457.341 223.75H-0.11254M457.341 237.75H-0.11254M457.341 251.75H-0.112541M457.341 265.75H-0.112541M457.341 279.75H-0.112542M457.341 293.75H-0.112543M457.341 307.75H-0.112543M457.341 321.75H-0.112544M457.341 335.751H-0.112545M457.341 349.751H-0.112545M457.341 363.751H-0.112546M457.341 377.751H-0.112546M457.341 391.751H-0.112547M457.341 405.751H-0.112548M457.341 419.751H-0.112548M457.341 433.751H-0.112549" stroke="#282828"/>
|
||||
<g filter="url(#filter0_f_467_4905)">
|
||||
<ellipse cx="0.312866" cy="214.736" rx="78.8492" ry="311.736" fill="#232323"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_f_467_4905)">
|
||||
<ellipse cx="390.849" cy="214.736" rx="78.8492" ry="311.736" fill="#232323"/>
|
||||
</g>
|
||||
<g filter="url(#filter2_f_467_4905)">
|
||||
<ellipse cx="194.736" cy="-0.0142959" rx="78.8492" ry="311.736" transform="rotate(-90 194.736 -0.0142959)" fill="#232323"/>
|
||||
</g>
|
||||
<g filter="url(#filter3_f_467_4905)">
|
||||
<ellipse cx="194.736" cy="430.849" rx="78.8492" ry="311.736" transform="rotate(-90 194.736 430.849)" fill="#232323"/>
|
||||
</g>
|
||||
<path opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M69.7673 129.848C69.7673 111.898 84.3181 97.3477 102.267 97.3477H290.044C307.993 97.3477 322.544 111.898 322.544 129.848V157.218C322.55 157.466 322.552 157.714 322.552 157.963V289.787C322.552 290.036 322.55 290.284 322.544 290.532V317.727C322.544 335.676 307.993 350.227 290.044 350.227H102.267C84.3181 350.227 69.7673 335.676 69.7673 317.727V232.148C69.5834 229.361 69.49 226.549 69.49 223.716C69.49 220.882 69.5834 218.071 69.7673 215.284V129.848ZM70.7673 232.114V215.317C71.8986 198.244 76.4539 182.107 83.7527 167.586V279.845C76.4539 265.325 71.8986 249.188 70.7673 232.114ZM70.7673 241.736V289.787C70.7673 301.431 77.0854 311.6 86.4804 317.052C84.7263 313.056 83.7527 308.641 83.7527 303.998V282.038C77.2909 269.667 72.8206 256.091 70.7673 241.736ZM84.7527 283.914V303.998C84.7527 308.971 85.905 313.674 87.9571 317.856C91.0513 319.437 94.4415 320.521 98.0224 321.004C97.9003 319.858 97.8377 318.695 97.8377 317.518V303.399C92.8997 297.345 88.5126 290.825 84.7527 283.914ZM98.8377 304.609V317.518C98.8377 318.737 98.907 319.941 99.0419 321.124C100.102 321.232 101.178 321.287 102.267 321.287H115.675C109.555 316.266 103.916 310.68 98.8377 304.609ZM117.261 321.287C110.509 315.856 104.333 309.739 98.8377 303.042V144.39C104.226 137.823 110.269 131.814 116.867 126.463H274.408C281.345 132.088 287.668 138.441 293.262 145.405V302.026C287.562 309.122 281.105 315.583 274.014 321.287H117.261ZM116.91 322.287H102.267C101.224 322.287 100.193 322.238 99.1747 322.142C99.7118 325.793 100.875 329.24 102.552 332.371C106.695 334.375 111.342 335.498 116.253 335.498H137.119C129.948 331.736 123.184 327.305 116.91 322.287ZM139.305 335.498C131.928 331.773 124.97 327.34 118.521 322.287H272.754C266.305 327.34 259.347 331.773 251.97 335.498H139.305ZM139.066 336.498H116.253C111.688 336.498 107.344 335.557 103.402 333.859C108.928 342.947 118.924 349.018 130.338 349.018H180.952C166.042 347.289 151.926 342.961 139.066 336.498ZM210.323 349.018H261.762C273.109 349.018 283.055 343.018 288.6 334.017C284.758 335.616 280.544 336.498 276.124 336.498H252.208C239.349 342.961 225.233 347.289 210.323 349.018ZM249.945 336.498C233.518 344.422 215.097 348.863 195.637 348.863C176.178 348.863 157.757 344.422 141.33 336.498H249.945ZM254.156 335.498H276.124C280.887 335.498 285.403 334.441 289.451 332.548C291.177 329.376 292.373 325.875 292.921 322.162C291.976 322.245 291.019 322.287 290.052 322.287H274.365C268.091 327.305 261.326 331.736 254.156 335.498ZM275.6 321.287H290.052C291.065 321.287 292.067 321.239 293.055 321.146C293.191 319.955 293.262 318.745 293.262 317.518V303.614C287.971 310.07 282.053 315.992 275.6 321.287ZM294.262 302.378V317.518C294.262 318.705 294.198 319.878 294.074 321.033C297.766 320.562 301.259 319.452 304.438 317.818C306.479 313.645 307.624 308.955 307.624 303.998V281.843C303.832 289.134 299.349 296.008 294.262 302.378ZM308.624 279.879V303.998C308.624 308.624 307.657 313.024 305.915 317.007C315.071 311.66 321.286 301.826 321.544 290.52V231.574C320.483 248.846 315.946 265.178 308.624 279.879ZM321.544 215.857V157.23C321.286 145.932 315.08 136.104 305.935 130.754C307.665 134.726 308.624 139.11 308.624 143.719V167.553C315.946 182.254 320.483 198.586 321.544 215.857ZM308.624 169.834C316.42 186.153 320.785 204.424 320.785 223.716C320.785 243.007 316.42 261.279 308.624 277.598V169.834ZM307.624 167.789V279.643C303.87 287.144 299.385 294.216 294.262 300.765V146.667C299.385 153.215 303.87 160.287 307.624 167.789ZM307.624 165.588V143.719C307.624 138.779 306.487 134.105 304.46 129.943C301.283 128.307 297.792 127.194 294.101 126.721C294.207 127.79 294.262 128.875 294.262 129.973V145.054C299.349 151.424 303.832 158.298 307.624 165.588ZM293.262 143.818V129.973C293.262 128.836 293.201 127.713 293.084 126.607C292.086 126.512 291.075 126.463 290.052 126.463H275.986C282.289 131.676 288.077 137.49 293.262 143.818ZM274.762 125.463H290.052C291.032 125.463 292.002 125.506 292.959 125.591C292.445 121.899 291.292 118.412 289.614 115.245C285.525 113.305 280.951 112.219 276.124 112.219H254.696C261.817 115.999 268.533 120.441 274.762 125.463ZM252.772 111.219H276.124C280.615 111.219 284.894 112.13 288.785 113.777C283.278 104.608 273.236 98.4727 261.762 98.4727H210.82C225.764 100.265 239.905 104.671 252.772 111.219ZM180.455 98.4727H130.338C118.796 98.4727 108.704 104.68 103.218 113.938C107.208 112.189 111.617 111.219 116.253 111.219H138.503C151.37 104.671 165.511 100.265 180.455 98.4727ZM140.743 111.219C157.317 103.116 175.946 98.5684 195.637 98.5684C215.329 98.5684 233.958 103.116 250.532 111.219H140.743ZM138.743 112.219H252.532C259.854 115.963 266.759 120.407 273.159 125.463H118.116C124.516 120.407 131.421 115.963 138.743 112.219ZM136.579 112.219H116.253C111.279 112.219 106.574 113.372 102.391 115.425C100.761 118.55 99.6398 121.982 99.1371 125.612C100.167 125.513 101.211 125.463 102.267 125.463H116.513C122.742 120.441 129.458 115.999 136.579 112.219ZM115.289 126.463H102.267C101.169 126.463 100.083 126.519 99.Line truncated
|
||||
<g filter="url(#filter4_bd_467_4905)">
|
||||
<path d="M192.144 125.816H138.679C130.173 125.816 122.52 130.986 119.345 138.877L99.0045 189.43C95.9432 197.038 97.6597 205.736 103.382 211.611L113.614 222.117C118.406 227.036 121.088 233.633 121.088 240.501L121.087 254.974C121.087 275.171 137.46 291.543 157.656 291.543C163.816 291.543 168.81 286.55 168.81 280.39L168.811 194.149C168.811 175.52 176.252 157.663 189.479 144.547C192.225 141.824 196.657 141.843 199.379 144.588C202.101 147.333 202.082 151.766 199.337 154.488C188.76 164.976 182.811 179.254 182.811 194.149L182.81 280.39C182.81 294.282 171.548 305.543 157.656 305.543C129.728 305.543 107.087 282.903 107.087 254.974L107.088 240.5C107.088 237.282 105.831 234.191 103.585 231.885L93.353 221.38C83.7626 211.533 80.8857 196.956 86.0164 184.204L106.357 133.651C111.665 120.459 124.458 111.816 138.679 111.816H194.408V111.9H204.747C253.851 111.9 293.657 151.706 293.657 200.81V251.652C293.657 255.518 290.523 258.652 286.657 258.652C282.791 258.652 279.657 255.518 279.657 251.652V200.81C279.657 159.438 246.119 125.9 204.747 125.9H193.23C192.86 125.9 192.498 125.871 192.144 125.816Z" fill="url(#paint0_linear_467_4905)" fill-opacity="0.07" shape-rendering="crispEdges"/>
|
||||
<path d="M210.03 283.94C210.03 280.074 206.896 276.94 203.03 276.94C199.164 276.94 196.03 280.074 196.03 283.94V287.053C196.03 314.012 217.884 335.867 244.843 335.867C271.194 335.867 292.668 314.988 293.624 288.871H318.238C322.104 288.871 325.238 285.737 325.238 281.871C325.238 278.005 322.104 274.871 318.238 274.871H291.397C260.653 274.871 231.141 262.788 209.224 241.228C206.468 238.517 202.036 238.553 199.325 241.309C196.614 244.065 196.65 248.497 199.406 251.209C221.131 272.58 249.522 285.632 279.634 288.343C278.955 306.972 263.639 321.867 244.843 321.867C225.616 321.867 210.03 306.28 210.03 287.053V283.94Z" fill="url(#paint1_linear_467_4905)" fill-opacity="0.07" shape-rendering="crispEdges"/>
|
||||
<path d="M238.03 202.145C238.03 206.937 241.915 210.822 246.707 210.822C251.499 210.822 255.383 206.937 255.383 202.145C255.383 197.353 251.499 193.469 246.707 193.469C241.915 193.469 238.03 197.353 238.03 202.145Z" fill="url(#paint2_linear_467_4905)" fill-opacity="0.07" shape-rendering="crispEdges"/>
|
||||
<path d="M192.144 125.816H138.679C130.173 125.816 122.52 130.986 119.345 138.877L99.0045 189.43C95.9432 197.038 97.6597 205.736 103.382 211.611L113.614 222.117C118.406 227.036 121.088 233.633 121.088 240.501L121.087 254.974C121.087 275.171 137.46 291.543 157.656 291.543C163.816 291.543 168.81 286.55 168.81 280.39L168.811 194.149C168.811 175.52 176.252 157.663 189.479 144.547C192.225 141.824 196.657 141.843 199.379 144.588C202.101 147.333 202.082 151.766 199.337 154.488C188.76 164.976 182.811 179.254 182.811 194.149L182.81 280.39C182.81 294.282 171.548 305.543 157.656 305.543C129.728 305.543 107.087 282.903 107.087 254.974L107.088 240.5C107.088 237.282 105.831 234.191 103.585 231.885L93.353 221.38C83.7626 211.533 80.8857 196.956 86.0164 184.204L106.357 133.651C111.665 120.459 124.458 111.816 138.679 111.816H194.408V111.9H204.747C253.851 111.9 293.657 151.706 293.657 200.81V251.652C293.657 255.518 290.523 258.652 286.657 258.652C282.791 258.652 279.657 255.518 279.657 251.652V200.81C279.657 159.438 246.119 125.9 204.747 125.9H193.23C192.86 125.9 192.498 125.871 192.144 125.816Z" stroke="#A0A0A0" stroke-width="0.5" shape-rendering="crispEdges"/>
|
||||
<path d="M210.03 283.94C210.03 280.074 206.896 276.94 203.03 276.94C199.164 276.94 196.03 280.074 196.03 283.94V287.053C196.03 314.012 217.884 335.867 244.843 335.867C271.194 335.867 292.668 314.988 293.624 288.871H318.238C322.104 288.871 325.238 285.737 325.238 281.871C325.238 278.005 322.104 274.871 318.238 274.871H291.397C260.653 274.871 231.141 262.788 209.224 241.228C206.468 238.517 202.036 238.553 199.325 241.309C196.614 244.065 196.65 248.497 199.406 251.209C221.131 272.58 249.522 285.632 279.634 288.343C278.955 306.972 263.639 321.867 244.843 321.867C225.616 321.867 210.03 306.28 210.03 287.053V283.94Z" stroke="#A0A0A0" stroke-width="0.5" shape-rendering="crispEdges"/>
|
||||
<path d="M238.03 202.145C238.03 206.937 241.915 210.822 246.707 210.822C251.499 210.822 255.383 206.937 255.383 202.145C255.383 197.353 251.499 193.469 246.707 193.469C241.915 193.469 238.03 197.353 238.03 202.145Z" stroke="#A0A0A0" stroke-width="0.5" shape-rendering="crispEdges"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_467_4905" x="-138.536" y="-157" width="277.698" height="743.472" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="30" result="effect1_foregroundBlur_467_4905"/>
|
||||
</filter>
|
||||
<filter id="filter1_f_467_4905" x="252" y="-157" width="277.698" height="743.472" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="30" result="effect1_foregroundBlur_467_4905"/>
|
||||
</filter>
|
||||
<filter id="filter2_f_467_4905" x="-177" y="-138.864" width="743.472" height="277.699" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="30" result="effect1_foregroundBlur_467_4905"/>
|
||||
</filter>
|
||||
<filter id="filter3_f_467_4905" x="-177" y="292" width="743.472" height="277.699" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="30" result="effect1_foregroundBlur_467_4905"/>
|
||||
</filter>
|
||||
<filter id="filter4_bd_467_4905" x="51.2566" y="79.5664" width="306.232" height="288.55" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feGaussianBlur in="BackgroundImageFix" stdDeviation="1"/>
|
||||
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_467_4905"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="16"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0313726 0 0 0 0 0.0470588 0 0 0 0 0.0470588 0 0 0 0.5 0"/>
|
||||
<feBlend mode="normal" in2="effect1_backgroundBlur_467_4905" result="effect2_dropShadow_467_4905"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_467_4905" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_467_4905" x1="146.817" y1="128.841" x2="146.817" y2="292.676" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#7C7C7C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_467_4905" x1="146.817" y1="128.841" x2="146.817" y2="292.676" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#7C7C7C"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_467_4905" x1="146.817" y1="128.841" x2="146.817" y2="292.676" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#7C7C7C"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_467_4905">
|
||||
<rect width="390" height="430" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 17 KiB |
@@ -5,35 +5,35 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Angelico de los Reyes at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Thu, 15 Dec 2022 22:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Thu, 15 Dec 2022 07:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-angelico_de_los_reyes-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgres-point-in-time-recovery</guid>
|
||||
<title>Point in Time Recovery is now available for Pro projects</title>
|
||||
<link>https://supabase.com/blog/postgres-point-in-time-recovery</link>
|
||||
<description>We're making PITR available for more projects, with a new Dashboard UI that makes it simple to use.</description>
|
||||
<pubDate>Thu, 15 Dec 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Thu, 15 Dec 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/continuous-postgresql-backup-walg</guid>
|
||||
<title>Continuous PostgreSQL Backups using WAL-G</title>
|
||||
<link>https://supabase.com/blog/continuous-postgresql-backup-walg</link>
|
||||
<description>Have you ever wanted to restore your database's state to a particular moment in time? This post explains how, using WAL-G.</description>
|
||||
<pubDate>Sat, 01 Aug 2020 21:00:00 GMT</pubDate>
|
||||
<pubDate>Sun, 02 Aug 2020 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgresql-templates</guid>
|
||||
<title>What are PostgreSQL Templates?</title>
|
||||
<link>https://supabase.com/blog/postgresql-templates</link>
|
||||
<description>What are PostgreSQL templates and what are they used for?</description>
|
||||
<pubDate>Wed, 08 Jul 2020 21:00:00 GMT</pubDate>
|
||||
<pubDate>Thu, 09 Jul 2020 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgresql-physical-logical-backups</guid>
|
||||
<title>Physical vs Logical Backups in PostgreSQL</title>
|
||||
<link>https://supabase.com/blog/postgresql-physical-logical-backups</link>
|
||||
<description>What are physical and logical backups in Postgres?</description>
|
||||
<pubDate>Mon, 06 Jul 2020 21:00:00 GMT</pubDate>
|
||||
<pubDate>Tue, 07 Jul 2020 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Ant Wilson at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Fri, 26 Feb 2021 22:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Sat, 27 Feb 2021 07:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-ant_wilson-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/cracking-postgres-interview</guid>
|
||||
<title>Cracking PostgreSQL Interview Questions</title>
|
||||
<link>https://supabase.com/blog/cracking-postgres-interview</link>
|
||||
<description>Understand the top PostgreSQL Interview Questions</description>
|
||||
<pubDate>Fri, 26 Feb 2021 22:00:00 GMT</pubDate>
|
||||
<pubDate>Sat, 27 Feb 2021 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Bo Lu at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Wed, 14 Dec 2022 22:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Wed, 14 Dec 2022 07:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-bo_lu-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgres-foreign-data-wrappers-rust</guid>
|
||||
<title>Supabase Wrappers, a Postgres FDW framework written in Rust</title>
|
||||
<link>https://supabase.com/blog/postgres-foreign-data-wrappers-rust</link>
|
||||
<description>A framework for building Postgres Foreign Data Wrappers which connects to Stripe, Firebase, Clickhouse, and more.</description>
|
||||
<pubDate>Wed, 14 Dec 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 14 Dec 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Mark Burggraf at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Wed, 23 Nov 2022 22:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Wed, 23 Nov 2022 07:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-burggraf-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/sql-or-nosql-both-with-postgresql</guid>
|
||||
<title>SQL or NoSQL? Why not use both (with PostgreSQL)?</title>
|
||||
<link>https://supabase.com/blog/sql-or-nosql-both-with-postgresql</link>
|
||||
<description>How to turn Postgres into an easy-to-use NoSQL database that retains all the power of SQL</description>
|
||||
<pubDate>Wed, 23 Nov 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 23 Nov 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgres-wasm</guid>
|
||||
<title>Postgres WASM by Snaplet and Supabase</title>
|
||||
<link>https://supabase.com/blog/postgres-wasm</link>
|
||||
<description>We're open sourcing postgres-wasm, a PostgresQL server that runs inside a browser, with our friends at Snaplet.</description>
|
||||
<pubDate>Sun, 02 Oct 2022 21:00:00 GMT</pubDate>
|
||||
<pubDate>Sun, 02 Oct 2022 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Egor Romanov at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Wed, 02 Aug 2023 21:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Wed, 02 Aug 2023 06:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-egor_romanov-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/fewer-dimensions-are-better-pgvector</guid>
|
||||
<title>pgvector: Fewer dimensions are better</title>
|
||||
<link>https://supabase.com/blog/fewer-dimensions-are-better-pgvector</link>
|
||||
<description>Increase performance in pgvector by using embedding vectors with fewer dimensions</description>
|
||||
<pubDate>Wed, 02 Aug 2023 21:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 02 Aug 2023 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/pgvector-performance</guid>
|
||||
<title>pgvector 0.4.0 performance</title>
|
||||
<link>https://supabase.com/blog/pgvector-performance</link>
|
||||
<description>There's been lot of talk about pgvector performance lately, so we took some datasets and pushed pgvector to the limits to find out its strengths and limitations.</description>
|
||||
<pubDate>Wed, 12 Jul 2023 21:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 12 Jul 2023 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Greg Richardson at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Wed, 02 Aug 2023 21:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Wed, 02 Aug 2023 06:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-gregnr-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/fewer-dimensions-are-better-pgvector</guid>
|
||||
<title>pgvector: Fewer dimensions are better</title>
|
||||
<link>https://supabase.com/blog/fewer-dimensions-are-better-pgvector</link>
|
||||
<description>Increase performance in pgvector by using embedding vectors with fewer dimensions</description>
|
||||
<pubDate>Wed, 02 Aug 2023 21:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 02 Aug 2023 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/openai-embeddings-postgres-vector</guid>
|
||||
<title>Storing OpenAI embeddings in Postgres with pgvector</title>
|
||||
<link>https://supabase.com/blog/openai-embeddings-postgres-vector</link>
|
||||
<description>An example of how to build an AI-powered search engine using OpenAI's embeddings and PostgreSQL.</description>
|
||||
<pubDate>Sun, 05 Feb 2023 22:00:00 GMT</pubDate>
|
||||
<pubDate>Sun, 05 Feb 2023 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Michel Pelletier at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Thu, 15 Dec 2022 22:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Thu, 15 Dec 2022 07:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-michel-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/vault-now-in-beta</guid>
|
||||
<title>Supabase Vault is now in Beta</title>
|
||||
<link>https://supabase.com/blog/vault-now-in-beta</link>
|
||||
<description>A Postgres extension to store encrypted secrets and encrypt data.</description>
|
||||
<pubDate>Thu, 15 Dec 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Thu, 15 Dec 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/transparent-column-encryption-with-postgres</guid>
|
||||
<title>Transparent Column Encryption with Postgres</title>
|
||||
<link>https://supabase.com/blog/transparent-column-encryption-with-postgres</link>
|
||||
<description>Using pgsodium's Transparent Column Encryption to encrypt data and provide your users with row-level encryption.</description>
|
||||
<pubDate>Wed, 30 Nov 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 30 Nov 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -5,56 +5,56 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Oliver Rice at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Wed, 02 Aug 2023 21:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Wed, 02 Aug 2023 06:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-oli_rice-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/fewer-dimensions-are-better-pgvector</guid>
|
||||
<title>pgvector: Fewer dimensions are better</title>
|
||||
<link>https://supabase.com/blog/fewer-dimensions-are-better-pgvector</link>
|
||||
<description>Increase performance in pgvector by using embedding vectors with fewer dimensions</description>
|
||||
<pubDate>Wed, 02 Aug 2023 21:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 02 Aug 2023 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/whats-new-in-pg-graphql-v1-2</guid>
|
||||
<title>What's New in pg_graphql v1.2</title>
|
||||
<link>https://supabase.com/blog/whats-new-in-pg-graphql-v1-2</link>
|
||||
<description>New Features in the v1.2 release of pg_graphql</description>
|
||||
<pubDate>Thu, 20 Apr 2023 21:00:00 GMT</pubDate>
|
||||
<pubDate>Thu, 20 Apr 2023 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/type-constraints-in-65-lines-of-sql</guid>
|
||||
<title>Type Constraints in 65 lines of SQL</title>
|
||||
<link>https://supabase.com/blog/type-constraints-in-65-lines-of-sql</link>
|
||||
<description>Creating validated data types in Postgres</description>
|
||||
<pubDate>Thu, 16 Feb 2023 22:00:00 GMT</pubDate>
|
||||
<pubDate>Thu, 16 Feb 2023 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/pg-graphql-v1</guid>
|
||||
<title>pg_graphql v1.0</title>
|
||||
<link>https://supabase.com/blog/pg-graphql-v1</link>
|
||||
<description>Announcing the v1.0 release of pg_graphql</description>
|
||||
<pubDate>Thu, 15 Dec 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Thu, 15 Dec 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgres-foreign-data-wrappers-rust</guid>
|
||||
<title>Supabase Wrappers, a Postgres FDW framework written in Rust</title>
|
||||
<link>https://supabase.com/blog/postgres-foreign-data-wrappers-rust</link>
|
||||
<description>A framework for building Postgres Foreign Data Wrappers which connects to Stripe, Firebase, Clickhouse, and more.</description>
|
||||
<pubDate>Wed, 14 Dec 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 14 Dec 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/pg-jsonschema-a-postgres-extension-for-json-validation</guid>
|
||||
<title>pg_jsonschema: JSON Schema support for Postgres</title>
|
||||
<link>https://supabase.com/blog/pg-jsonschema-a-postgres-extension-for-json-validation</link>
|
||||
<description>Today we're releasing pg_jsonschema, a Postgres extension for JSON validation.</description>
|
||||
<pubDate>Thu, 18 Aug 2022 21:00:00 GMT</pubDate>
|
||||
<pubDate>Thu, 18 Aug 2022 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgres-audit</guid>
|
||||
<title>Postgres Auditing in 150 lines of SQL</title>
|
||||
<link>https://supabase.com/blog/postgres-audit</link>
|
||||
<description>PostgreSQL has a robust set of features which we can leverage to create a generic auditing solution in 150 lines of SQL.</description>
|
||||
<pubDate>Mon, 07 Mar 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Mon, 07 Mar 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -5,49 +5,49 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Paul Copplestone at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Sun, 30 Apr 2023 21:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Sun, 30 Apr 2023 06:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-paul_copplestone-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgres-pluggable-strorage</guid>
|
||||
<title>Next steps for Postgres pluggable storage</title>
|
||||
<link>https://supabase.com/blog/postgres-pluggable-strorage</link>
|
||||
<description>Exploring history of Postgres pluggable storage and the possibility of landing it in the Postgres core.</description>
|
||||
<pubDate>Sun, 30 Apr 2023 21:00:00 GMT</pubDate>
|
||||
<pubDate>Sun, 30 Apr 2023 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgres-foreign-data-wrappers-rust</guid>
|
||||
<title>Supabase Wrappers, a Postgres FDW framework written in Rust</title>
|
||||
<link>https://supabase.com/blog/postgres-foreign-data-wrappers-rust</link>
|
||||
<description>A framework for building Postgres Foreign Data Wrappers which connects to Stripe, Firebase, Clickhouse, and more.</description>
|
||||
<pubDate>Wed, 14 Dec 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 14 Dec 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgres-crdt</guid>
|
||||
<title>pg_crdt - an experimental CRDT extension for Postgres</title>
|
||||
<link>https://supabase.com/blog/postgres-crdt</link>
|
||||
<description>Embedding Yjs and Automerge into Postgres for collaborative applications.</description>
|
||||
<pubDate>Fri, 09 Dec 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Fri, 09 Dec 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/partial-postgresql-data-dumps-with-rls</guid>
|
||||
<title>Partial data dumps using Postgres Row Level Security</title>
|
||||
<link>https://supabase.com/blog/partial-postgresql-data-dumps-with-rls</link>
|
||||
<description>Using RLS to create seed files for local PostgreSQL testing.</description>
|
||||
<pubDate>Mon, 27 Jun 2022 21:00:00 GMT</pubDate>
|
||||
<pubDate>Mon, 27 Jun 2022 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgres-as-a-cron-server</guid>
|
||||
<title>Postgres as a CRON Server</title>
|
||||
<link>https://supabase.com/blog/postgres-as-a-cron-server</link>
|
||||
<description>Running repetitive tasks with your Postgres database.</description>
|
||||
<pubDate>Thu, 04 Mar 2021 22:00:00 GMT</pubDate>
|
||||
<pubDate>Fri, 05 Mar 2021 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgresql-views</guid>
|
||||
<title>Postgres Views</title>
|
||||
<link>https://supabase.com/blog/postgresql-views</link>
|
||||
<description>Creating and using a view in PostgreSQL.</description>
|
||||
<pubDate>Tue, 17 Nov 2020 22:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 18 Nov 2020 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -5,28 +5,28 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Pavel Borisov at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Wed, 12 Jul 2023 21:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Wed, 12 Jul 2023 06:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-pavel-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/pgvector-performance</guid>
|
||||
<title>pgvector 0.4.0 performance</title>
|
||||
<link>https://supabase.com/blog/pgvector-performance</link>
|
||||
<description>There's been lot of talk about pgvector performance lately, so we took some datasets and pushed pgvector to the limits to find out its strengths and limitations.</description>
|
||||
<pubDate>Wed, 12 Jul 2023 21:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 12 Jul 2023 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/new-in-postgres-15</guid>
|
||||
<title>What's new in Postgres 15?</title>
|
||||
<link>https://supabase.com/blog/new-in-postgres-15</link>
|
||||
<description>Describes the release of Postgres 15, new features and reasons to use it</description>
|
||||
<pubDate>Thu, 15 Dec 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Thu, 15 Dec 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgresql-commitfest</guid>
|
||||
<title>What is PostgreSQL commitfest and how to contribute</title>
|
||||
<link>https://supabase.com/blog/postgresql-commitfest</link>
|
||||
<description>A time-tested method for contributing to the core Postgres code</description>
|
||||
<pubDate>Wed, 26 Oct 2022 21:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 26 Oct 2022 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Steve Chavez at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Thu, 15 Dec 2022 22:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Thu, 15 Dec 2022 07:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-steve_chavez-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgrest-11-prerelease</guid>
|
||||
<title>PostgREST 11 pre-release</title>
|
||||
<link>https://supabase.com/blog/postgrest-11-prerelease</link>
|
||||
<description>Describes new features of PostgREST 11 pre-release</description>
|
||||
<pubDate>Thu, 15 Dec 2022 22:00:00 GMT</pubDate>
|
||||
<pubDate>Thu, 15 Dec 2022 07:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/roles-postgres-hooks</guid>
|
||||
<title>Protecting reserved roles with PostgreSQL Hooks</title>
|
||||
<link>https://supabase.com/blog/roles-postgres-hooks</link>
|
||||
<description>Using Postgres Hooks to protect functionality in your Postgres database.</description>
|
||||
<pubDate>Thu, 01 Jul 2021 21:00:00 GMT</pubDate>
|
||||
<pubDate>Thu, 01 Jul 2021 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -5,28 +5,28 @@
|
||||
<link>https://supabase.com/blog</link>
|
||||
<description>Latest Postgres news from Victor at Supabase</description>
|
||||
<language>en</language>
|
||||
<lastBuildDate>Thu, 13 Oct 2022 21:00:00 GMT</lastBuildDate>
|
||||
<lastBuildDate>Thu, 13 Oct 2022 06:00:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://supabase.com/planetpg-victor-rss.xml" rel="self" type="application/rss+xml"/>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/postgres-full-text-search-vs-the-rest</guid>
|
||||
<title>Postgres Full Text Search vs the rest</title>
|
||||
<link>https://supabase.com/blog/postgres-full-text-search-vs-the-rest</link>
|
||||
<description>Comparing one of the most popular Postgres features against alternatives</description>
|
||||
<pubDate>Thu, 13 Oct 2022 21:00:00 GMT</pubDate>
|
||||
<pubDate>Thu, 13 Oct 2022 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/choosing-a-postgres-primary-key</guid>
|
||||
<title>Choosing a Postgres Primary Key</title>
|
||||
<link>https://supabase.com/blog/choosing-a-postgres-primary-key</link>
|
||||
<description>Turns out the question of which identifier to use as a Primary Key is complicated -- we're going to dive into some of the complexity and inherent trade-offs, and figure things out</description>
|
||||
<pubDate>Wed, 07 Sep 2022 21:00:00 GMT</pubDate>
|
||||
<pubDate>Wed, 07 Sep 2022 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
<item>
|
||||
<guid>https://supabase.com/blog/seen-by-in-postgresql</guid>
|
||||
<title>Implementing "seen by" functionality with Postgres</title>
|
||||
<link>https://supabase.com/blog/seen-by-in-postgresql</link>
|
||||
<description>Different approaches for tracking visitor counts with PostgreSQL.</description>
|
||||
<pubDate>Sun, 17 Jul 2022 21:00:00 GMT</pubDate>
|
||||
<pubDate>Sun, 17 Jul 2022 06:00:00 GMT</pubDate>
|
||||
</item>
|
||||
|
||||
</channel>
|
||||
|
||||
@@ -94,12 +94,10 @@ FUNCTIONS_VERIFY_JWT=false
|
||||
LOGFLARE_LOGGER_BACKEND_API_KEY=your-super-secret-and-long-logflare-key
|
||||
|
||||
# Change vector.toml sinks to reflect this change
|
||||
LOGFLARE_HTTP_PORT=4001
|
||||
LOGFLARE_API_KEY=your-super-secret-and-long-logflare-key
|
||||
|
||||
# Change vector.toml sources.docker_syslog to reflect this change
|
||||
VECTOR_PORT=9000
|
||||
VECTOR_API_PORT=9001
|
||||
# Docker socket location - this value will differ depending on your OS
|
||||
DOCKER_SOCKET_LOCATION=/var/run/docker.sock
|
||||
|
||||
# Google Cloud Project details
|
||||
GOOGLE_PROJECT_ID=GOOGLE_PROJECT_ID
|
||||
|
||||
@@ -23,11 +23,6 @@ services:
|
||||
timeout: 5s
|
||||
interval: 5s
|
||||
retries: 3
|
||||
logging:
|
||||
driver: syslog
|
||||
options:
|
||||
syslog-address: "tcp://localhost:${VECTOR_PORT}"
|
||||
tag: studio
|
||||
depends_on:
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
@@ -60,11 +55,6 @@ services:
|
||||
ports:
|
||||
- ${KONG_HTTP_PORT}:8000/tcp
|
||||
- ${KONG_HTTPS_PORT}:8443/tcp
|
||||
logging:
|
||||
driver: syslog
|
||||
options:
|
||||
syslog-address: "tcp://localhost:${VECTOR_PORT}"
|
||||
tag: kong
|
||||
depends_on:
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
@@ -107,11 +97,6 @@ services:
|
||||
interval: 5s
|
||||
retries: 3
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: syslog
|
||||
options:
|
||||
syslog-address: "tcp://localhost:${VECTOR_PORT}"
|
||||
tag: auth
|
||||
environment:
|
||||
GOTRUE_API_HOST: 0.0.0.0
|
||||
GOTRUE_API_PORT: 9999
|
||||
@@ -159,11 +144,6 @@ services:
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: syslog
|
||||
options:
|
||||
syslog-address: "tcp://localhost:${VECTOR_PORT}"
|
||||
tag: rest
|
||||
environment:
|
||||
PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||
PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS}
|
||||
@@ -172,6 +152,7 @@ services:
|
||||
PGRST_DB_USE_LEGACY_GUCS: "false"
|
||||
PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET}
|
||||
PGRST_APP_SETTINGS_JWT_EXP: ${JWT_EXPIRY}
|
||||
command: "postgrest"
|
||||
|
||||
realtime:
|
||||
container_name: realtime-dev.supabase-realtime
|
||||
@@ -194,11 +175,6 @@ services:
|
||||
interval: 5s
|
||||
retries: 3
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: syslog
|
||||
options:
|
||||
syslog-address: "tcp://localhost:${VECTOR_PORT}"
|
||||
tag: realtime
|
||||
environment:
|
||||
PORT: 4000
|
||||
DB_HOST: ${POSTGRES_HOST}
|
||||
@@ -287,11 +263,6 @@ services:
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: syslog
|
||||
options:
|
||||
syslog-address: "tcp://localhost:${VECTOR_PORT}"
|
||||
tag: meta
|
||||
environment:
|
||||
PG_META_PORT: 8080
|
||||
PG_META_DB_HOST: ${POSTGRES_HOST}
|
||||
@@ -302,16 +273,11 @@ services:
|
||||
|
||||
functions:
|
||||
container_name: supabase-edge-functions
|
||||
image: supabase/edge-runtime:v1.8.1
|
||||
image: supabase/edge-runtime:v1.13.1
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
analytics:
|
||||
condition: service_healthy
|
||||
logging:
|
||||
driver: syslog
|
||||
options:
|
||||
syslog-address: "tcp://localhost:${VECTOR_PORT}"
|
||||
tag: functions
|
||||
environment:
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
SUPABASE_URL: http://kong:8000
|
||||
@@ -368,6 +334,8 @@ services:
|
||||
# Uncomment to use Big Query backend for analytics
|
||||
# GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID}
|
||||
# GOOGLE_PROJECT_NUMBER: ${GOOGLE_PROJECT_NUMBER}
|
||||
ports:
|
||||
- 4000:4000
|
||||
entrypoint: |
|
||||
sh -c `cat <<'EOF' > run.sh && sh run.sh
|
||||
./logflare eval Logflare.Release.migrate
|
||||
@@ -387,11 +355,6 @@ services:
|
||||
depends_on:
|
||||
vector:
|
||||
condition: service_healthy
|
||||
logging:
|
||||
driver: syslog
|
||||
options:
|
||||
syslog-address: "tcp://localhost:${VECTOR_PORT}"
|
||||
tag: db
|
||||
command:
|
||||
- postgres
|
||||
- -c
|
||||
@@ -433,14 +396,13 @@ services:
|
||||
"--no-verbose",
|
||||
"--tries=1",
|
||||
"--spider",
|
||||
"http://localhost:${VECTOR_API_PORT}/health"
|
||||
"http://vector:${VECTOR_API_PORT}/health"
|
||||
]
|
||||
timeout: 5s
|
||||
interval: 5s
|
||||
retries: 3
|
||||
ports:
|
||||
- ${VECTOR_PORT}:${VECTOR_PORT}
|
||||
- ${VECTOR_API_PORT}:${VECTOR_API_PORT}
|
||||
volumes:
|
||||
- ./volumes/logs/vector.yml:/etc/vector/vector.yml:ro
|
||||
- ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock:ro
|
||||
|
||||
command: [ "--config", "etc/vector/vector.yml" ]
|
||||
@@ -1,41 +1,43 @@
|
||||
api:
|
||||
enabled: true
|
||||
address: '0.0.0.0:9001'
|
||||
address: 0.0.0.0:9001
|
||||
|
||||
sources:
|
||||
docker_syslog:
|
||||
type: 'syslog'
|
||||
address: '0.0.0.0:9000'
|
||||
mode: 'tcp'
|
||||
path: '/tmp/socket'
|
||||
docker_host:
|
||||
type: docker_logs
|
||||
exclude_containers:
|
||||
- supabase-vector
|
||||
|
||||
transforms:
|
||||
project_logs:
|
||||
type: remap
|
||||
inputs:
|
||||
- docker_syslog
|
||||
- docker_host
|
||||
source: |-
|
||||
.project = "default"
|
||||
.event_message = del(.message)
|
||||
del(.procid)
|
||||
del(.source_id)
|
||||
.appname = del(.container_name)
|
||||
del(.container_created_at)
|
||||
del(.container_id)
|
||||
del(.source_type)
|
||||
del(.facility)
|
||||
del(.stream)
|
||||
del(.label)
|
||||
del(.image)
|
||||
del(.host)
|
||||
del(.id)
|
||||
del(.stream)
|
||||
router:
|
||||
type: route
|
||||
inputs:
|
||||
- project_logs
|
||||
route:
|
||||
kong: '.appname == "kong"'
|
||||
auth: '.appname == "auth"'
|
||||
rest: '.appname == "rest"'
|
||||
realtime: '.appname == "realtime"'
|
||||
storage: '.appname == "storage"'
|
||||
functions: '.appname == "functions"'
|
||||
db: '.appname == "db"'
|
||||
# Kong logs only include api requests
|
||||
kong: '.appname == "supabase-kong"'
|
||||
auth: '.appname == "supabase-auth"'
|
||||
rest: '.appname == "supabase-rest"'
|
||||
realtime: '.appname == "supabase-realtime"'
|
||||
storage: '.appname == "supabase-storage"'
|
||||
functions: '.appname == "supabase-functions"'
|
||||
db: '.appname == "supabase-db"'
|
||||
# Ignores non nginx errors since they are related with kong booting up
|
||||
kong_logs:
|
||||
type: remap
|
||||
inputs:
|
||||
@@ -52,7 +54,10 @@ transforms:
|
||||
.metadata.request.protocol = req.protocol
|
||||
.metadata.response.status_code = req.status
|
||||
}
|
||||
# TODO: create a separate page and filter for kong error logs
|
||||
if err != null {
|
||||
abort
|
||||
}
|
||||
# Ignores non nginx errors since they are related with kong booting up
|
||||
kong_err:
|
||||
type: remap
|
||||
inputs:
|
||||
@@ -73,6 +78,9 @@ transforms:
|
||||
.metadata.request.protocol = url[2]
|
||||
}
|
||||
}
|
||||
if err != null {
|
||||
abort
|
||||
}
|
||||
# Gotrue logs are structured json strings which frontend parses directly. But we keep metadata for consistency.
|
||||
auth_logs:
|
||||
type: remap
|
||||
@@ -126,7 +134,6 @@ transforms:
|
||||
.metadata.context[0].pid = parsed.pid
|
||||
}
|
||||
# Postgres logs some messages to stderr which we map to warning severity level
|
||||
# TODO: parse raw postgres logs via regex
|
||||
db_logs:
|
||||
type: remap
|
||||
inputs:
|
||||
@@ -134,7 +141,16 @@ transforms:
|
||||
source: |-
|
||||
.metadata.host = "db-default"
|
||||
.metadata.parsed.timestamp = .timestamp
|
||||
.metadata.parsed.error_severity = replace!(.severity, r'^err$', "warning")
|
||||
parsed, err = parse_regex(.event_message, r'.*(?P<level>INFO|NOTICE|WARNING|ERROR|LOG|FATAL|PANIC?):.*', numeric_groups: true)
|
||||
|
||||
if err != null || parsed == null {
|
||||
.metadata.parsed.error_severity = "info"
|
||||
}
|
||||
if parsed != null {
|
||||
.metadata.parsed.error_severity = parsed.level
|
||||
}
|
||||
|
||||
|
||||
|
||||
sinks:
|
||||
logflare_auth:
|
||||
@@ -204,6 +220,7 @@ sinks:
|
||||
type: 'http'
|
||||
inputs:
|
||||
- kong_logs
|
||||
- kong_err
|
||||
encoding:
|
||||
codec: 'json'
|
||||
method: 'post'
|
||||
|
||||
@@ -14210,10 +14210,6 @@
|
||||
"websocket": "^1.0.34"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase/shared-types": {
|
||||
"version": "0.1.43",
|
||||
"license": "Copyright Supabase"
|
||||
},
|
||||
"node_modules/@supabase/storage-js": {
|
||||
"version": "2.5.1",
|
||||
"license": "MIT",
|
||||
@@ -42495,7 +42491,7 @@
|
||||
"@supabase/auth-helpers-nextjs": "^0.5.6",
|
||||
"@supabase/auth-helpers-react": "^0.3.1",
|
||||
"@supabase/react-data-grid": "^7.1.0-beta.7",
|
||||
"@supabase/shared-types": "^0.1.43",
|
||||
"@supabase/shared-types": "^0.1.46",
|
||||
"@supabase/supabase-js": "^2.23.0",
|
||||
"@tanstack/react-query": "^4.22.0",
|
||||
"@tanstack/react-query-devtools": "^4.22.0",
|
||||
@@ -42816,6 +42812,11 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
}
|
||||
},
|
||||
"studio/node_modules/@supabase/shared-types": {
|
||||
"version": "0.1.46",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/shared-types/-/shared-types-0.1.46.tgz",
|
||||
"integrity": "sha512-DV3VIQcyWdkVhQJNkH2IzHTiDS37AM5DnI/kQZc6YlzS93cFHERkwueKDFFIl85Q0UddG6ANZuoTne3w7BjWOQ=="
|
||||
},
|
||||
"studio/node_modules/@tanstack/match-sorter-utils": {
|
||||
"version": "8.7.6",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export const detectBrowser = () => {
|
||||
if (!navigator) return undefined
|
||||
|
||||
if (navigator.userAgent.indexOf('Chrome') !== -1) {
|
||||
return 'Chrome'
|
||||
} else if (navigator.userAgent.indexOf('Firefox') !== -1) {
|
||||
return 'Firefox'
|
||||
} else if (navigator.userAgent.indexOf('Safari') !== -1) {
|
||||
return 'Safari'
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import SidebarLayout from './layouts/SidebarLayout'
|
||||
|
||||
export * from './Providers'
|
||||
export * from './helpers'
|
||||
export * from './hooks'
|
||||
export * from './gotrue'
|
||||
export * from './auth'
|
||||
|
||||
@@ -11,7 +11,6 @@ export interface PricingInformation {
|
||||
description: string
|
||||
preface: string
|
||||
features: string[]
|
||||
featuresOrg?: string[]
|
||||
footer?: string
|
||||
cta: string
|
||||
}
|
||||
@@ -21,12 +20,12 @@ export const plans: PricingInformation[] = [
|
||||
id: 'tier_free',
|
||||
name: 'Free',
|
||||
nameBadge: '',
|
||||
costUnit: '/ month / project',
|
||||
costUnitOrg: '/ month',
|
||||
href: 'https://supabase.com/dashboard/new/new-project',
|
||||
costUnit: '/ month / org',
|
||||
costUnitOrg: '/ month / org',
|
||||
href: 'https://supabase.com/dashboard/new?plan=free',
|
||||
priceLabel: '',
|
||||
priceMonthly: 0,
|
||||
warning: 'Limit of 2 free projects',
|
||||
warning: 'Limit of 2 free organizations',
|
||||
description: 'Perfect for passion projects & simple websites.',
|
||||
preface: 'Get started with:',
|
||||
features: [
|
||||
@@ -34,7 +33,7 @@ export const plans: PricingInformation[] = [
|
||||
'Social OAuth providers',
|
||||
'Up to 500MB database space',
|
||||
'Up to 1GB file storage',
|
||||
'Up to 2GB bandwidth',
|
||||
'Up to 5GB bandwidth',
|
||||
'Up to 50MB file uploads',
|
||||
'Up to 50,000 monthly active users',
|
||||
'Up to 500K Edge Function invocations',
|
||||
@@ -50,34 +49,19 @@ export const plans: PricingInformation[] = [
|
||||
id: 'tier_pro',
|
||||
name: 'Pro',
|
||||
nameBadge: '',
|
||||
costUnit: '/ month / project',
|
||||
costUnitOrg: '/ month',
|
||||
href: 'https://supabase.com/dashboard/new/new-project',
|
||||
costUnit: '/ month / org',
|
||||
costUnitOrg: '/ month / org',
|
||||
href: 'https://supabase.com/dashboard/new?plan=pro',
|
||||
priceLabel: 'From',
|
||||
warning: 'Usage-based plan',
|
||||
priceMonthly: 25,
|
||||
description: 'For production applications with the option to scale.',
|
||||
featuresOrg: [
|
||||
'$10 Compute Credits',
|
||||
'No project pausing',
|
||||
'Daily backups stored for 7 days',
|
||||
'8GB database space included',
|
||||
'100GB file storage included',
|
||||
'50GB bandwidth included',
|
||||
'5GB file uploads included',
|
||||
'100,000 monthly active users included',
|
||||
'2M Edge Function invocations included',
|
||||
'500 concurrent Realtime connections included',
|
||||
'5 million Realtime messages included',
|
||||
'7-day log retention',
|
||||
'Email support',
|
||||
],
|
||||
features: [
|
||||
'No project pausing',
|
||||
'Daily backups stored for 7 days',
|
||||
'8GB database space included',
|
||||
'100GB file storage included',
|
||||
'50GB bandwidth included',
|
||||
'250GB bandwidth included',
|
||||
'5GB file uploads included',
|
||||
'100,000 monthly active users included',
|
||||
'2M Edge Function invocations included',
|
||||
@@ -94,9 +78,9 @@ export const plans: PricingInformation[] = [
|
||||
id: 'tier_team',
|
||||
name: 'Team',
|
||||
nameBadge: 'New',
|
||||
costUnit: '/ month',
|
||||
costUnitOrg: '/ month',
|
||||
href: 'https://forms.supabase.com/team',
|
||||
costUnit: '/ month / org',
|
||||
costUnitOrg: '/ month / org',
|
||||
href: 'https://supabase.com/dashboard/new?plan=team',
|
||||
priceLabel: 'From',
|
||||
warning: 'Usage-based plan',
|
||||
priceMonthly: 599,
|
||||
@@ -113,7 +97,7 @@ export const plans: PricingInformation[] = [
|
||||
],
|
||||
footer: 'Additional fees apply for usage beyond included usage.',
|
||||
preface: 'Everything in the Pro plan, plus:',
|
||||
cta: 'Contact Us',
|
||||
cta: 'Get Started',
|
||||
},
|
||||
{
|
||||
id: 'tier_enterprise',
|
||||
@@ -122,10 +106,10 @@ export const plans: PricingInformation[] = [
|
||||
description: 'For large-scale applications managing serious workloads.',
|
||||
features: [
|
||||
`Designated Support manager & SLAs`,
|
||||
`Enterprise OAuth providers`,
|
||||
`SSO/SAML`,
|
||||
`On-premise support`,
|
||||
`24×7×365 premium enterprise support`,
|
||||
'Custom Security Questionnaires',
|
||||
`Private Slack channel`,
|
||||
],
|
||||
priceLabel: '',
|
||||
|
||||
@@ -112,14 +112,14 @@ export const pricing: Pricing = {
|
||||
usage_based: false,
|
||||
},
|
||||
{
|
||||
title: 'Database egress',
|
||||
title: 'Bandwidth',
|
||||
tooltips: {
|
||||
main: 'Billing is based on the total sum of outgoing traffic of your database in GB throughout your billing period.',
|
||||
main: 'Billing is based on the total sum of outgoing traffic (includes Database, Storage, Realtime, Auth) in GB throughout your billing period.',
|
||||
},
|
||||
plans: {
|
||||
free: '2GB included',
|
||||
pro: '50 GB included, then $0.09 per GB',
|
||||
team: '50 GB included, then $0.09 per GB',
|
||||
free: '5GB included',
|
||||
pro: '250 GB included, then $0.09 per GB',
|
||||
team: '250 GB included, then $0.09 per GB',
|
||||
enterprise: 'Unlimited',
|
||||
},
|
||||
usage_based: true,
|
||||
@@ -184,16 +184,6 @@ export const pricing: Pricing = {
|
||||
},
|
||||
usage_based: false,
|
||||
},
|
||||
{
|
||||
title: 'Enterprise OAuth providers',
|
||||
plans: {
|
||||
free: false,
|
||||
pro: false,
|
||||
team: false,
|
||||
enterprise: true,
|
||||
},
|
||||
usage_based: false,
|
||||
},
|
||||
{
|
||||
title: 'Audit trails',
|
||||
plans: {
|
||||
@@ -244,19 +234,6 @@ export const pricing: Pricing = {
|
||||
},
|
||||
usage_based: true,
|
||||
},
|
||||
{
|
||||
title: 'Storage egress',
|
||||
tooltips: {
|
||||
main: 'All requests to view and download your storage items go through our CDN. We sum up all outgoing traffic (egress) for storage related requests through our CDN. We do not differentiate between cache and no cache hits.\nBilling is based on the total amount of egress in GB throughout your billing period.',
|
||||
},
|
||||
plans: {
|
||||
free: '2 GB included',
|
||||
pro: '200 GB included, then $0.09 per GB',
|
||||
team: '200 GB included, then $0.09 per GB',
|
||||
enterprise: 'Unlimited',
|
||||
},
|
||||
usage_based: true,
|
||||
},
|
||||
{
|
||||
title: 'Custom access controls',
|
||||
plans: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
clispec: '001'
|
||||
info:
|
||||
id: cli
|
||||
version: 1.88.0
|
||||
version: 1.93.0
|
||||
title: Supabase CLI
|
||||
language: sh
|
||||
source: https://github.com/supabase/cli
|
||||
@@ -1481,7 +1481,7 @@ commands:
|
||||
summary: Show total table sizes, including table index sizes
|
||||
description: |2-
|
||||
|
||||
This command displays the total size of each table in the database. It is the sum of the values that `pg_table_size()` and `pg_indexes_size()` gives for each table.
|
||||
This command displays the total size of each table in the database. It is the sum of the values that `pg_table_size()` and `pg_indexes_size()` gives for each table. System tables inside `pg_catalog` and `information_schema` are not included.
|
||||
|
||||
```
|
||||
NAME │ SIZE
|
||||
@@ -2249,7 +2249,7 @@ commands:
|
||||
default_value: ''
|
||||
- id: supabase-db
|
||||
title: supabase db
|
||||
summary: Manage local Postgres databases
|
||||
summary: Manage Postgres databases
|
||||
tags:
|
||||
- local-dev
|
||||
links: []
|
||||
|
||||
@@ -3,6 +3,9 @@ POSTGRES_PASSWORD=your-super-secret-and-long-postgres-password
|
||||
|
||||
DEFAULT_ORGANIZATION_NAME=Default Organization
|
||||
DEFAULT_PROJECT_NAME=Default Project
|
||||
DASHBOARD_USERNAME=supabase
|
||||
DASHBOARD_PASSWORD=this_password_is_insecure_and_should_be_updated
|
||||
|
||||
|
||||
SUPABASE_URL=http://localhost:8000
|
||||
SUPABASE_PUBLIC_URL=http://localhost:8000
|
||||
@@ -20,3 +23,5 @@ NEXT_PUBLIC_HCAPTCHA_SITE_KEY=10000000-ffff-ffff-ffff-000000000001
|
||||
# CmdK / AI
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhndWloeHV6cWlid3hqbmlteGV2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NzUwOTQ4MzUsImV4cCI6MTk5MDY3MDgzNX0.0PMlOxtKL4O9GGZuAP_Xl4f-Tut1qOnW4bNEmAtoB8w
|
||||
NEXT_PUBLIC_SUPABASE_URL=https://xguihxuzqibwxjnimxev.supabase.co
|
||||
|
||||
DOCKER_SOCKET_LOCATION=/var/run/docker.sock
|
||||
@@ -0,0 +1,23 @@
|
||||
import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, IconAlertTriangle } from 'ui'
|
||||
|
||||
export function EmailRateLimitsAlert() {
|
||||
return (
|
||||
<Alert_Shadcn_ variant="warning">
|
||||
<IconAlertTriangle strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>Built-in email service is rate-limited!</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
You're using the built-in email service. The service has rate limits and it's not meant to
|
||||
be used for production apps. Check the{' '}
|
||||
<a
|
||||
href="https://supabase.com/docs/guides/platform/going-into-prod#auth-rate-limits"
|
||||
className="underline"
|
||||
target="_blank"
|
||||
>
|
||||
documentation
|
||||
</a>{' '}
|
||||
for an up-to-date information on the current rate limits. Please use a custom SMTP server if
|
||||
you're planning on having large number of users.
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { EmailRateLimitsAlert } from './EmailRateLimitsAlert'
|
||||
|
||||
export default EmailRateLimitsAlert
|
||||
@@ -14,6 +14,7 @@ import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
|
||||
import { TEMPLATES_SCHEMAS } from 'stores/authConfig/schema'
|
||||
import TemplateEditor from './TemplateEditor'
|
||||
import EmailRateLimitsAlert from '../EmailRateLimitsAlert'
|
||||
|
||||
const EmailTemplates = () => {
|
||||
const { ref: projectRef } = useParams()
|
||||
@@ -25,6 +26,11 @@ const EmailTemplates = () => {
|
||||
isSuccess,
|
||||
} = useAuthConfigQuery({ projectRef })
|
||||
|
||||
const builtInSMTP =
|
||||
isSuccess &&
|
||||
authConfig &&
|
||||
(!authConfig.SMTP_HOST || !authConfig.SMTP_USER || !authConfig.SMTP_PASS)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
@@ -65,6 +71,11 @@ const EmailTemplates = () => {
|
||||
const panelId = template.title.trim().replace(/\s+/g, '-')
|
||||
return (
|
||||
<Tabs.Panel id={panelId} label={template.title} key={panelId}>
|
||||
{builtInSMTP ? (
|
||||
<div className="mx-8">
|
||||
<EmailRateLimitsAlert />
|
||||
</div>
|
||||
) : null}
|
||||
<TemplateEditor
|
||||
key={template.title}
|
||||
template={template}
|
||||
|
||||
@@ -83,5 +83,23 @@ FOR DELETE USING (
|
||||
check: '',
|
||||
command: 'DELETE',
|
||||
roles: [],
|
||||
}, {
|
||||
id: 'policy-5',
|
||||
templateName: 'Enable insert access for users based on their user ID *',
|
||||
description:
|
||||
'This policy assumes that your table has a column "user_id", and allows users to insert rows which the "user_id" column matches their ID',
|
||||
statement: `
|
||||
CREATE POLICY "policy_name"
|
||||
ON ${schema}.${table}
|
||||
FOR INSERT USING (
|
||||
auth.uid() = user_id
|
||||
) WITH CHECK (
|
||||
auth.uid() = user_id
|
||||
);`.trim(),
|
||||
name: 'Enable insert for users based on user_id',
|
||||
definition: 'auth.uid() = user_id',
|
||||
check: 'true',
|
||||
command: 'INSERT',
|
||||
roles: [],
|
||||
},
|
||||
]
|
||||
@@ -33,6 +33,7 @@ import { useCheckPermissions, useStore } from 'hooks'
|
||||
import { urlRegex } from './../Auth.constants'
|
||||
import { defaultDisabledSmtpFormValues } from './SmtpForm.constants'
|
||||
import { generateFormValues, isSmtpEnabled } from './SmtpForm.utils'
|
||||
import EmailRateLimitsAlert from '../EmailRateLimitsAlert'
|
||||
|
||||
const SmtpForm = () => {
|
||||
const { ui } = useStore()
|
||||
@@ -231,23 +232,7 @@ const SmtpForm = () => {
|
||||
)
|
||||
) : (
|
||||
<div className="mx-8 mb-8 -mt-4">
|
||||
<Alert_Shadcn_ variant="warning">
|
||||
<IconAlertTriangle strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>Built-in email service is rate-limited!</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
You're using the built-in email service. The service has rate limits and it's
|
||||
not meant to be used for production apps. Check the{' '}
|
||||
<a
|
||||
href="https://supabase.com/docs/guides/platform/going-into-prod#auth-rate-limits"
|
||||
className="underline"
|
||||
target="_blank"
|
||||
>
|
||||
documentation
|
||||
</a>{' '}
|
||||
for an up-to-date information on the current rate limits. Please use a custom
|
||||
SMTP server if you're planning on having large number of users.
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
<EmailRateLimitsAlert />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ const UserDropdown = ({ user, canRemoveUser, canRemoveMFAFactors }: UserDropdown
|
||||
) : null}
|
||||
<Dropdown.Separator />
|
||||
<Tooltip.Root delayDuration={0}>
|
||||
<Tooltip.Trigger>
|
||||
<Tooltip.Trigger asChild>
|
||||
<Dropdown.Item
|
||||
onClick={handleDeleteFactors}
|
||||
icon={<IconShieldOff size="tiny" />}
|
||||
@@ -160,7 +160,7 @@ const UserDropdown = ({ user, canRemoveUser, canRemoveMFAFactors }: UserDropdown
|
||||
)}
|
||||
</Tooltip.Root>
|
||||
<Tooltip.Root delayDuration={0}>
|
||||
<Tooltip.Trigger>
|
||||
<Tooltip.Trigger asChild>
|
||||
<Dropdown.Item
|
||||
onClick={handleDelete}
|
||||
icon={<IconTrash size="tiny" />}
|
||||
|
||||
@@ -34,12 +34,12 @@ const SpendCapModal = ({ visible, onHide }: SpendCapModalProps) => {
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<p className="w-[50%] text-sm">Database size</p>
|
||||
<p className="w-[25%] text-sm">8GB</p>
|
||||
<p className="w-[25%] text-sm">$0.125/GB</p>
|
||||
<p className="w-[25%] text-sm">$0.125 per GB</p>
|
||||
</div>
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<p className="w-[50%] text-sm">Database egress</p>
|
||||
<p className="w-[25%] text-sm">50GB</p>
|
||||
<p className="w-[25%] text-sm">$0.09/GB</p>
|
||||
<p className="w-[50%] text-sm">Egress</p>
|
||||
<p className="w-[25%] text-sm">250GB</p>
|
||||
<p className="w-[25%] text-sm">$0.09 per GB</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-2">
|
||||
@@ -75,10 +75,8 @@ const SpendCapModal = ({ visible, onHide }: SpendCapModalProps) => {
|
||||
</Tooltip.Root>
|
||||
</div>
|
||||
<p className="w-[25%] text-sm">100,000</p>
|
||||
<p className="w-[25%] text-sm">$0.00325/user</p>
|
||||
<p className="w-[25%] text-sm">$0.00325 per user</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<div className="flex w-[50%] items-center space-x-2">
|
||||
<p className="text-sm">Single Sign-On (SAML 2.0) MAUs</p>
|
||||
@@ -111,19 +109,15 @@ const SpendCapModal = ({ visible, onHide }: SpendCapModalProps) => {
|
||||
</Tooltip.Root>
|
||||
</div>
|
||||
<p className="w-[25%] text-sm">50</p>
|
||||
<p className="w-[25%] text-sm">$0.015/user</p>
|
||||
<p className="w-[25%] text-sm">$0.015 per user</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="py-2">
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<p className="w-[50%] text-sm">Storage size</p>
|
||||
<p className="w-[25%] text-sm">100GB</p>
|
||||
<p className="w-[25%] text-sm">$0.021/GB</p>
|
||||
</div>
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<p className="w-[50%] text-sm">Storage egress</p>
|
||||
<p className="w-[25%] text-sm">200GB</p>
|
||||
<p className="w-[25%] text-sm">$0.09/GB</p>
|
||||
<p className="w-[25%] text-sm">$0.021 per GB</p>
|
||||
</div>
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<p className="w-[50%] text-sm">Storage Image Transformations</p>
|
||||
@@ -136,13 +130,13 @@ const SpendCapModal = ({ visible, onHide }: SpendCapModalProps) => {
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<p className="w-[50%] text-sm">Realtime concurrent peak connections</p>
|
||||
<p className="w-[25%] text-sm">500</p>
|
||||
<p className="w-[25%] text-sm">$10/1000</p>
|
||||
<p className="w-[25%] text-sm">$10 per 1000</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center px-4 py-1">
|
||||
<p className="w-[50%] text-sm">Realtime messages</p>
|
||||
<p className="w-[25%] text-sm">5 Million</p>
|
||||
<p className="w-[25%] text-sm">$2.50/Million</p>
|
||||
<p className="w-[25%] text-sm">$2.50 per Million</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -161,7 +155,7 @@ const SpendCapModal = ({ visible, onHide }: SpendCapModalProps) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<p className="text-sm">
|
||||
See{' '}
|
||||
<Link href="https://supabase.com/pricing" passHref>
|
||||
<a className="text-brand" target="_blank" rel="noreferrer">
|
||||
|
||||
@@ -55,15 +55,13 @@ const BackupItem = ({ projectRef, backup, index }: BackupItemProps) => {
|
||||
if (backup.status === 'COMPLETED')
|
||||
return (
|
||||
<div className="flex space-x-4">
|
||||
{backup.data.canRestore && (
|
||||
<Button
|
||||
type="default"
|
||||
disabled={!canTriggerScheduledBackups || isRestoring}
|
||||
onClick={onRestoreClick}
|
||||
>
|
||||
Restore
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="default"
|
||||
disabled={!canTriggerScheduledBackups || isRestoring}
|
||||
onClick={onRestoreClick}
|
||||
>
|
||||
Restore
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
return <Badge color="yellow">Backup In Progress...</Badge>
|
||||
|
||||
@@ -96,6 +96,7 @@ const EditHookPanel = ({ visible, selectedHook, onClose }: EditHookPanelProps) =
|
||||
function_type: isEdgeFunction(selectedHook?.function_args?.[0] ?? '')
|
||||
? 'supabase_function'
|
||||
: 'http_request',
|
||||
timeout_ms: Number(selectedHook?.function_args?.[4] ?? 1000),
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -168,6 +169,10 @@ const EditHookPanel = ({ visible, selectedHook, onClose }: EditHookPanelProps) =
|
||||
}
|
||||
}
|
||||
|
||||
if (values.timeout_ms < 1000 || values.timeout_ms > 5000) {
|
||||
errors['timeout_ms'] = 'Timeout should be between 1000ms and 5000ms'
|
||||
}
|
||||
|
||||
if (JSON.stringify(values) !== JSON.stringify(initialValues)) setIsEdited(true)
|
||||
return errors
|
||||
}
|
||||
@@ -185,7 +190,6 @@ const EditHookPanel = ({ visible, selectedHook, onClose }: EditHookPanelProps) =
|
||||
return ui.setNotification({ category: 'error', message: 'Unable to find selected table' })
|
||||
}
|
||||
|
||||
const serviceTimeoutMs = '1000'
|
||||
const headers = httpHeaders
|
||||
.filter((header) => header.name && header.value)
|
||||
.reduce((a: any, b: any) => {
|
||||
@@ -215,7 +219,7 @@ const EditHookPanel = ({ visible, selectedHook, onClose }: EditHookPanelProps) =
|
||||
values.http_method,
|
||||
JSON.stringify(headers),
|
||||
JSON.stringify(parameters),
|
||||
serviceTimeoutMs,
|
||||
values.timeout_ms.toString(),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,14 @@ const HTTPRequestFields = ({
|
||||
})}
|
||||
</Listbox>
|
||||
) : null}
|
||||
<Input
|
||||
id="timeout_ms"
|
||||
name="timeout_ms"
|
||||
label="Timeout"
|
||||
labelOptional="Between 1000ms to 5000ms"
|
||||
type="number"
|
||||
actions={<p className="text-light pr-2">ms</p>}
|
||||
/>
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
<SidePanel.Separator />
|
||||
|
||||
@@ -8,7 +8,6 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { useCheckPermissions } from 'hooks'
|
||||
import SchemaTable from './SchemaTable'
|
||||
import AlphaPreview from 'components/to-be-cleaned/AlphaPreview'
|
||||
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
|
||||
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { useParams } from 'common'
|
||||
import { OrganizationPaymentMethod } from 'data/organizations/organization-payment-methods-query'
|
||||
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
|
||||
import {
|
||||
SubscriptionTier,
|
||||
useOrgSubscriptionUpdateMutation,
|
||||
} from 'data/subscriptions/org-subscription-update-mutation'
|
||||
import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation'
|
||||
import { SubscriptionTier } from 'data/subscriptions/types'
|
||||
import { useStore } from 'hooks'
|
||||
import { Button, Modal } from 'ui'
|
||||
|
||||
|
||||
@@ -7,10 +7,7 @@ import { useEffect, useState } from 'react'
|
||||
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
||||
import { useOrgPlansQuery } from 'data/subscriptions/org-plans-query'
|
||||
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
|
||||
import {
|
||||
SubscriptionTier,
|
||||
useOrgSubscriptionUpdateMutation,
|
||||
} from 'data/subscriptions/org-subscription-update-mutation'
|
||||
import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation'
|
||||
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
|
||||
import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
@@ -27,6 +24,7 @@ import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-l
|
||||
import { useOrganizationBillingSubscriptionPreview } from 'data/organizations/organization-billing-subscription-preview'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { SubscriptionTier } from 'data/subscriptions/types'
|
||||
|
||||
// [Joshen TODO] Need to remove all contexts of "projects"
|
||||
|
||||
@@ -266,7 +264,7 @@ const PlanUpdateSidePanel = () => {
|
||||
<div className="border-t my-6" />
|
||||
|
||||
<ul role="list">
|
||||
{(plan.featuresOrg || plan.features).map((feature) => (
|
||||
{plan.features.map((feature) => (
|
||||
<li key={feature} className="flex py-2">
|
||||
<div className="w-[12px]">
|
||||
<IconCheck
|
||||
@@ -304,7 +302,7 @@ const PlanUpdateSidePanel = () => {
|
||||
<Modal
|
||||
loading={isUpdating}
|
||||
alignFooter="right"
|
||||
className="!w-[550px]"
|
||||
size="large"
|
||||
visible={selectedTier !== undefined && selectedTier !== 'tier_free'}
|
||||
onCancel={() => setSelectedTier(undefined)}
|
||||
onConfirm={onUpdateSubscription}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import Link from 'next/link'
|
||||
@@ -20,7 +21,7 @@ import {
|
||||
import { SpendCapModal } from 'components/interfaces/BillingV2'
|
||||
import { useOrganizationBillingMigrationMutation } from 'data/organizations/organization-migrate-billing-mutation'
|
||||
import { useOrganizationBillingMigrationPreview } from 'data/organizations/organization-migrate-billing-preview-query'
|
||||
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
|
||||
import { useCheckPermissions, useFlag, useSelectedOrganization, useStore } from 'hooks'
|
||||
import { PRICING_TIER_LABELS_ORG } from 'lib/constants'
|
||||
import PaymentMethodSelection from '../BillingSettingsV2/Subscription/PaymentMethodSelection'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
@@ -30,10 +31,13 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
const router = useRouter()
|
||||
const organization = useSelectedOrganization()
|
||||
|
||||
const disableOrgBillingMigration = useFlag('disableOrgBillingMigration')
|
||||
const canMigrateOrg = useCheckPermissions(PermissionAction.UPDATE, 'organizations')
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [tier, setTier] = useState('')
|
||||
const [showSpendCapHelperModal, setShowSpendCapHelperModal] = useState(false)
|
||||
const [isSpendCapEnabled, setIsSpendCapEnabled] = useState(true)
|
||||
const [isSpendCapEnabled, setIsSpendCapEnabled] = useState(false)
|
||||
const [paymentMethodId, setPaymentMethodId] = useState('')
|
||||
|
||||
const dbTier = useMemo(() => {
|
||||
@@ -65,6 +69,7 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
data: migrationPreviewData,
|
||||
error: migrationPreviewError,
|
||||
isLoading: migrationPreviewIsLoading,
|
||||
isSuccess: migrationPreviewIsSuccess,
|
||||
remove,
|
||||
refetch: previewMigration,
|
||||
} = useOrganizationBillingMigrationPreview(
|
||||
@@ -90,6 +95,23 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
|
||||
const canMigrateOrganization = useCheckPermissions(PermissionAction.UPDATE, 'organizations')
|
||||
|
||||
const selectedLimitedUsage = useMemo(
|
||||
() => tier === 'PRO' && isSpendCapEnabled,
|
||||
[tier, isSpendCapEnabled]
|
||||
)
|
||||
|
||||
const downgradingToLimitedUsage = useMemo(() => {
|
||||
if (migrationPreviewData) {
|
||||
const hadUsageBillingEnabled = migrationPreviewData.old_tiers.some((it) =>
|
||||
['tier_payg', 'tier_team', 'tier_enterprise'].includes(it)
|
||||
)
|
||||
|
||||
return hadUsageBillingEnabled && selectedLimitedUsage
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}, [migrationPreviewData, selectedLimitedUsage])
|
||||
|
||||
const toggle = () => {
|
||||
setIsOpen(!isOpen)
|
||||
}
|
||||
@@ -108,9 +130,37 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Button loading={!organization?.slug} onClick={() => setIsOpen(true)} type="primary">
|
||||
Migrate organization
|
||||
</Button>
|
||||
<Tooltip.Root delayDuration={0}>
|
||||
<Tooltip.Trigger>
|
||||
<Button
|
||||
loading={!organization?.slug}
|
||||
onClick={toggle}
|
||||
type="primary"
|
||||
disabled={!canMigrateOrg || disableOrgBillingMigration}
|
||||
>
|
||||
Migrate organization
|
||||
</Button>
|
||||
</Tooltip.Trigger>
|
||||
{(!canMigrateOrg || disableOrgBillingMigration) && (
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content side="bottom">
|
||||
<Tooltip.Arrow className="radix-tooltip-arrow" />
|
||||
<div
|
||||
className={[
|
||||
'rounded bg-scale-100 py-1 px-2 leading-none shadow', // background
|
||||
'border border-scale-200 ', //border
|
||||
].join(' ')}
|
||||
>
|
||||
<span className="text-xs text-scale-1200">
|
||||
{!canMigrateOrg
|
||||
? 'You need additional permissions to migrate this organization'
|
||||
: 'Migrations are temporarily disabled, please try again later.'}
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
)}
|
||||
</Tooltip.Root>
|
||||
</div>
|
||||
<Modal
|
||||
closable
|
||||
@@ -125,48 +175,41 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
}
|
||||
>
|
||||
<div className="space-y-4 py-3">
|
||||
{migrationPreviewData?.addons_to_be_removed &&
|
||||
migrationPreviewData.addons_to_be_removed.length > 0 && (
|
||||
<>
|
||||
<Modal.Content>
|
||||
<div className="space-y-2">
|
||||
<Alert_Shadcn_ variant="warning">
|
||||
<IconAlertTriangle strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>Project addons will be removed</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
<div>
|
||||
The following project addons will be removed when downgrading
|
||||
<ul className="list-disc list-inside pl-4">
|
||||
{migrationPreviewData.addons_to_be_removed.map((addon) =>
|
||||
addon.addons.map((variant) => (
|
||||
<li key={`${addon.projectRef}-${variant.variant}`}>
|
||||
{variant.type === 'pitr'
|
||||
? 'PITR - '
|
||||
: variant.type === 'compute_instance'
|
||||
? 'Compute Instance - '
|
||||
: ''}
|
||||
{variant.name} for project {addon.projectName || addon.projectRef}
|
||||
</li>
|
||||
))
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
</div>
|
||||
</Modal.Content>
|
||||
<Modal.Separator />
|
||||
</>
|
||||
)}
|
||||
<Modal.Content>
|
||||
<div className="space-y-2">
|
||||
<Alert_Shadcn_ variant="destructive">
|
||||
<IconAlertCircle strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>Irreversible</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
Once migrated to the new organization-based billing, you cannot go back to the old
|
||||
project-level billing.
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
|
||||
{migrationPreviewData?.addons_to_be_removed &&
|
||||
migrationPreviewData.addons_to_be_removed.length > 0 && (
|
||||
<Alert_Shadcn_ variant="warning">
|
||||
<IconAlertTriangle strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>Project addons will be removed</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
<div>
|
||||
The following project addons will be removed when downgrading
|
||||
<ul className="list-disc list-inside pl-4">
|
||||
{migrationPreviewData.addons_to_be_removed.map((addon) =>
|
||||
addon.addons.map((variant) => (
|
||||
<li key={`${addon.projectRef}-${variant.variant}`}>
|
||||
{variant.type === 'pitr'
|
||||
? 'PITR - '
|
||||
: variant.type === 'compute_instance'
|
||||
? 'Compute Instance - '
|
||||
: ''}
|
||||
{variant.name} for project {addon.projectName || addon.projectRef}
|
||||
</li>
|
||||
))
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
</div>
|
||||
</Modal.Content>
|
||||
<Modal.Separator />
|
||||
<Modal.Content>
|
||||
<div className="text-scale-1000 text-sm space-y-2">
|
||||
<div className="text-scale-1100 text-sm space-y-2">
|
||||
<p>
|
||||
Migrating to new organization-based billing combines subscriptions for all projects
|
||||
in the organization into a single subscription.
|
||||
@@ -180,7 +223,7 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
</a>
|
||||
</Link>
|
||||
. To transfer projects to a different organization, visit{' '}
|
||||
<Link href="/projects/_/settings/general">
|
||||
<Link href="/project/_/settings/general">
|
||||
<a target="_blank" rel="noreferrer" className="underline">
|
||||
General settings
|
||||
</a>
|
||||
@@ -211,7 +254,7 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
})}
|
||||
</Listbox>
|
||||
|
||||
<p className="text-sm text-scale-1000 mt-4">
|
||||
<p className="text-sm text-scale-1100 mt-4">
|
||||
The pricing plan, along with included usage limits will apply to your entire
|
||||
organization. See{' '}
|
||||
<a
|
||||
@@ -229,7 +272,7 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
<div className="my-2 space-y-1 pb-4">
|
||||
<p className="text-sm text-scale-1000">
|
||||
Paid plans come with one compute instance included. Additional projects will at
|
||||
least cost the compute instance hours used (min $7/month). See{' '}
|
||||
least cost the compute instance hours used (min $10/month). See{' '}
|
||||
<Link href="https://supabase.com/docs/guides/platform/org-based-billing#usage-based-billing-for-compute">
|
||||
<a target="_blank" rel="noreferrer" className="underline">
|
||||
Compute Instance Usage Billing
|
||||
@@ -271,8 +314,8 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
<div className="col-span-12">
|
||||
<p className="text-sm text-scale-1000">
|
||||
When enabled, usage is limited to the plan's quota, with restrictions when
|
||||
limits are exceeded. To scale beyond Pro limits without restrictions, disable
|
||||
the spend cap and pay for over-usage beyond the quota.
|
||||
limits are exceeded. When disabled, you scale beyond Pro limits without
|
||||
restrictions and pay for over-usage beyond the quota.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -300,15 +343,46 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
|
||||
<Modal.Content>
|
||||
<Loading active={tier !== '' && migrationPreviewIsLoading}>
|
||||
{migrationPreviewError && (
|
||||
<Alert_Shadcn_ variant="destructive">
|
||||
<IconAlertCircle strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>Organization cannot be migrated</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
{migrationPreviewError.message}
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
<div className="space-y-3">
|
||||
{migrationPreviewError && (
|
||||
<Alert_Shadcn_ variant="destructive">
|
||||
<IconAlertCircle strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>Organization cannot be migrated</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
{migrationPreviewError.message}
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
|
||||
{(downgradingToLimitedUsage ||
|
||||
(!downgradingToLimitedUsage && selectedLimitedUsage)) && (
|
||||
<Alert_Shadcn_ variant="warning">
|
||||
<IconAlertCircle strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>Spend Cap is enabled</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
<div className="space-y-2">
|
||||
{downgradingToLimitedUsage ? (
|
||||
<p>
|
||||
You previously had the spend cap disabled to scale seamlessly beyond the
|
||||
included quota. You will run into restrictions in case you exceed the
|
||||
included quota.
|
||||
</p>
|
||||
) : (
|
||||
<p>
|
||||
With the Spend Cap enabled, your projects will be restricted once you
|
||||
exhaust the included quota. To scale seamlessly beyond the included
|
||||
quota, disable the Spend Cap.
|
||||
</p>
|
||||
)}
|
||||
|
||||
<Button type="outline" onClick={() => setIsSpendCapEnabled(false)}>
|
||||
Disable Spend Cap
|
||||
</Button>
|
||||
</div>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
</div>
|
||||
</Loading>
|
||||
|
||||
{migrationError && (
|
||||
@@ -320,7 +394,7 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
)}
|
||||
</Modal.Content>
|
||||
|
||||
{!migrationPreviewIsLoading && migrationPreviewData && dbTier !== 'tier_free' && (
|
||||
{migrationPreviewIsSuccess && dbTier !== 'tier_free' && (
|
||||
<Modal.Content>
|
||||
<InformationBox
|
||||
defaultVisibility={false}
|
||||
@@ -389,13 +463,23 @@ const MigrateOrganizationBillingButton = observer(() => {
|
||||
)}
|
||||
|
||||
<Modal.Content>
|
||||
<p className="mb-4 text-sm text-scale-1100">
|
||||
The migration can take up to 30 seconds, please do not cancel the request or close
|
||||
your browser.
|
||||
</p>
|
||||
|
||||
<Button
|
||||
block
|
||||
size="small"
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
loading={isMigrating}
|
||||
disabled={migrationPreviewData === undefined || isMigrating || !tier}
|
||||
disabled={
|
||||
migrationPreviewData === undefined ||
|
||||
isMigrating ||
|
||||
!tier ||
|
||||
disableOrgBillingMigration
|
||||
}
|
||||
onClick={() => onConfirmMigrate()}
|
||||
>
|
||||
I understand, migrate this organization
|
||||
|
||||
@@ -23,10 +23,8 @@ const OrganizationBillingMigrationPanel = observer(() => {
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-scale-1100">
|
||||
You will have a single subscription for all projects inside the organization, rather
|
||||
than individual subscriptions per project.
|
||||
</p>
|
||||
<p className="text-sm text-scale-1100">
|
||||
Migrating your organization billing is not reversible.
|
||||
than individual subscriptions per project. Migrating to organization-based billing
|
||||
is not reversible.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 ml-12">
|
||||
|
||||
@@ -23,6 +23,7 @@ import { invalidateOrganizationsQuery } from 'data/organizations/organizations-q
|
||||
import { useStore } from 'hooks'
|
||||
import { BASE_PATH, PRICING_TIER_LABELS_ORG } from 'lib/constants'
|
||||
import { getURL } from 'lib/helpers'
|
||||
import { useParams } from 'common'
|
||||
|
||||
const ORG_KIND_TYPES = {
|
||||
PERSONAL: 'Personal',
|
||||
@@ -57,6 +58,8 @@ const NewOrgForm = ({ onPaymentMethodReset }: NewOrgFormProps) => {
|
||||
const stripe = useStripe()
|
||||
const elements = useElements()
|
||||
|
||||
const { plan } = useParams()
|
||||
|
||||
const [orgName, setOrgName] = useState('')
|
||||
const [orgKind, setOrgKind] = useState(ORG_KIND_DEFAULT)
|
||||
const [orgSize, setOrgSize] = useState(ORG_SIZE_DEFAULT)
|
||||
@@ -64,7 +67,10 @@ const NewOrgForm = ({ onPaymentMethodReset }: NewOrgFormProps) => {
|
||||
const [newOrgLoading, setNewOrgLoading] = useState(false)
|
||||
const [paymentMethod, setPaymentMethod] = useState<PaymentMethod>()
|
||||
|
||||
const [dbPricingTierKey, setDbPricingTierKey] = useState('FREE')
|
||||
// URL param support for passing plan
|
||||
const [dbPricingTierKey, setDbPricingTierKey] = useState(
|
||||
plan && ['free', 'team', 'pro'].includes(plan) ? plan.toUpperCase() : 'FREE'
|
||||
)
|
||||
|
||||
const [showSpendCapHelperModal, setShowSpendCapHelperModal] = useState(false)
|
||||
const [isSpendCapEnabled, setIsSpendCapEnabled] = useState(true)
|
||||
@@ -133,7 +139,7 @@ const NewOrgForm = ({ onPaymentMethodReset }: NewOrgFormProps) => {
|
||||
elements,
|
||||
redirect: 'if_required',
|
||||
confirmParams: {
|
||||
return_url: `${getURL()}/new-with-subscription`,
|
||||
return_url: `${getURL()}/new`,
|
||||
expand: ['payment_method'],
|
||||
},
|
||||
})
|
||||
|
||||
@@ -88,13 +88,7 @@ const ConnectionPooling = () => {
|
||||
) : (
|
||||
<PgbouncerConfig
|
||||
projectRef={projectRef}
|
||||
// [Joshen TODO] remove this check once API PR has been deployed:
|
||||
// https://github.com/supabase/infrastructure/pull/14173
|
||||
bouncerInfo={{
|
||||
...bouncerInfo,
|
||||
supavisor_enabled:
|
||||
poolingConfiguration.connectionString.includes('pooler.supabase.com'),
|
||||
}}
|
||||
bouncerInfo={bouncerInfo}
|
||||
connectionInfo={connectionInfo}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -3,13 +3,13 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
import { Button, IconPause } from 'ui'
|
||||
import { Button, IconPause, Modal } from 'ui'
|
||||
|
||||
import {
|
||||
useIsProjectActive,
|
||||
useProjectContext,
|
||||
} from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog'
|
||||
import ConfirmationModal from 'components/ui/ConfirmationModal'
|
||||
import { useProjectPauseMutation } from 'data/projects/project-pause-mutation'
|
||||
import { setProjectStatus } from 'data/projects/projects-query'
|
||||
import { useCheckPermissions, useStore } from 'hooks'
|
||||
@@ -86,16 +86,24 @@ const PauseProjectButton = () => {
|
||||
</Tooltip.Portal>
|
||||
) : null}
|
||||
</Tooltip.Root>
|
||||
<ConfirmModal
|
||||
<ConfirmationModal
|
||||
danger
|
||||
visible={isModalOpen}
|
||||
title="Pause this project?"
|
||||
description="Are you sure you want to pause this project? It will not be accessible until you unpause it."
|
||||
loading={isPausing}
|
||||
header="Pause this project?"
|
||||
description=""
|
||||
buttonLabel="Pause project"
|
||||
buttonLoadingLabel="Pausing project"
|
||||
onSelectCancel={() => setIsModalOpen(false)}
|
||||
onSelectConfirm={requestPauseProject}
|
||||
/>
|
||||
>
|
||||
<Modal.Content className="py-4">
|
||||
<p className="text-light text-sm">
|
||||
Are you sure you want to pause this project? It will not be accessible until you unpause
|
||||
it.
|
||||
</p>
|
||||
</Modal.Content>
|
||||
</ConfirmationModal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
|
||||
import { useProjectTransferMutation } from 'data/projects/project-transfer-mutation'
|
||||
import { useProjectTransferPreviewQuery } from 'data/projects/project-transfer-preview-query'
|
||||
import { useCheckPermissions, useSelectedProject, useStore } from 'hooks'
|
||||
import { useCheckPermissions, useFlag, useSelectedProject, useStore } from 'hooks'
|
||||
|
||||
const TransferProjectButton = () => {
|
||||
const { ui } = useStore()
|
||||
@@ -27,6 +27,7 @@ const TransferProjectButton = () => {
|
||||
const projectRef = project?.ref
|
||||
const projectOrgId = project?.organization_id
|
||||
const { data: allOrganizations } = useOrganizationsQuery()
|
||||
const disableProjectTransfer = useFlag('disableProjectTransfer')
|
||||
|
||||
const organizations = (allOrganizations || [])
|
||||
.filter((it) => it.id !== projectOrgId)
|
||||
@@ -87,11 +88,15 @@ const TransferProjectButton = () => {
|
||||
<>
|
||||
<Tooltip.Root delayDuration={0}>
|
||||
<Tooltip.Trigger>
|
||||
<Button onClick={toggle} type="default" disabled={!canTransferProject}>
|
||||
<Button
|
||||
onClick={toggle}
|
||||
type="default"
|
||||
disabled={!canTransferProject || disableProjectTransfer}
|
||||
>
|
||||
Transfer project
|
||||
</Button>
|
||||
</Tooltip.Trigger>
|
||||
{!canTransferProject && (
|
||||
{(!canTransferProject || disableProjectTransfer) && (
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content side="bottom">
|
||||
<Tooltip.Arrow className="radix-tooltip-arrow" />
|
||||
@@ -102,7 +107,9 @@ const TransferProjectButton = () => {
|
||||
].join(' ')}
|
||||
>
|
||||
<span className="text-xs text-scale-1200">
|
||||
You need additional permissions to transfer this project
|
||||
{!canTransferProject
|
||||
? 'You need additional permissions to transfer this project'
|
||||
: 'Project transfers are temporarily disabled, please try again later.'}
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
@@ -137,7 +144,13 @@ const TransferProjectButton = () => {
|
||||
<Modal.Content>
|
||||
<p className="text-sm">
|
||||
To transfer projects, the owner must be a member of both the source and target
|
||||
organizations.
|
||||
organizations. For further information see our{' '}
|
||||
<Link href="https://supabase.com/docs/guides/platform/project-transfer">
|
||||
<a className="text-brand hover:underline" target="_blank" rel="noreferrer">
|
||||
Documentation
|
||||
</a>
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
|
||||
<p className="font-bold mt-6 text-sm">Transferring considerations:</p>
|
||||
|
||||
@@ -70,6 +70,9 @@ const CreateBucketModal = ({ visible, onClose }: CreateBucketModalProps) => {
|
||||
if (values.has_file_size_limit && values.formatted_size_limit < 0) {
|
||||
errors.formatted_size_limit = 'File size upload limit has to be at least 0'
|
||||
}
|
||||
if (values.name === 'public') {
|
||||
errors.name = '"public" is a reserved name. Please choose another name'
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
|
||||
@@ -1,48 +1,50 @@
|
||||
import { SupportCategories } from '@supabase/shared-types/out/constants'
|
||||
|
||||
export const CATEGORY_OPTIONS = [
|
||||
{
|
||||
value: 'Problem',
|
||||
value: SupportCategories.PROBLEM,
|
||||
label: 'Issues with APIs / client libraries',
|
||||
description: "Issues with your project's API and client libraries",
|
||||
query: undefined,
|
||||
},
|
||||
{
|
||||
value: 'Dashboard_bug',
|
||||
value: SupportCategories.DASHBOARD_BUG,
|
||||
label: 'Dashboard bug',
|
||||
description: 'Issues with the Supabase dashboard',
|
||||
query: undefined,
|
||||
},
|
||||
{
|
||||
value: 'Database_unresponsive',
|
||||
value: SupportCategories.DATABASE_UNRESPONSIVE,
|
||||
label: 'Database unresponsive',
|
||||
description: 'Issues with connecting to your database',
|
||||
query: 'Unable to connect',
|
||||
},
|
||||
{
|
||||
value: 'Performance',
|
||||
value: SupportCategories.PERFORMANCE_ISSUES,
|
||||
label: 'Performance issues',
|
||||
description: 'Reporting of performance issues is only available on the Pro plan',
|
||||
query: 'Performance',
|
||||
},
|
||||
{
|
||||
value: 'Sales',
|
||||
value: SupportCategories.SALES_ENQUIRY,
|
||||
label: 'Sales enquiry',
|
||||
description: 'Questions about pricing, paid plans and Enterprise plans',
|
||||
query: undefined,
|
||||
},
|
||||
{
|
||||
value: 'Billing',
|
||||
value: SupportCategories.BILLING,
|
||||
label: 'Billing',
|
||||
description: 'Issues with credit card charges | invoices | overcharging',
|
||||
query: undefined,
|
||||
},
|
||||
{
|
||||
value: 'Problem',
|
||||
value: SupportCategories.ABUSE,
|
||||
label: 'Abuse report',
|
||||
description: 'Report abuse of a Supabase project or Supabase brand',
|
||||
query: undefined,
|
||||
},
|
||||
{
|
||||
value: 'Refund',
|
||||
value: SupportCategories.REFUND,
|
||||
label: 'Refund enquiry',
|
||||
description: 'Formal enquiry form for requesting refunds',
|
||||
query: undefined,
|
||||
|
||||
@@ -22,11 +22,10 @@ import {
|
||||
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
||||
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
|
||||
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
|
||||
import { useFlag, useSelectedOrganization } from 'hooks'
|
||||
import { useSelectedOrganization } from 'hooks'
|
||||
|
||||
const OrganizationDropdown = () => {
|
||||
const router = useRouter()
|
||||
const orgCreationV2 = useFlag('orgcreationv2')
|
||||
const selectedOrganization = useSelectedOrganization()
|
||||
const { data: organizations, isLoading: isLoadingOrganizations } = useOrganizationsQuery()
|
||||
|
||||
@@ -97,13 +96,13 @@ const OrganizationDropdown = () => {
|
||||
</ScrollArea>
|
||||
</CommandGroup_Shadcn_>
|
||||
<CommandGroup_Shadcn_ className="border-t">
|
||||
<Link passHref href={orgCreationV2 ? `/new-with-subscription` : `/new`}>
|
||||
<Link passHref href="new">
|
||||
<CommandItem_Shadcn_
|
||||
asChild
|
||||
className="cursor-pointer flex items-center space-x-2 w-full"
|
||||
onSelect={(e) => {
|
||||
setOpen(false)
|
||||
router.push(orgCreationV2 ? `/new-with-subscription` : `/new`)
|
||||
router.push(`/new`)
|
||||
}}
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
|
||||
@@ -38,7 +38,7 @@ const LayoutHeader = ({ customHeaderComponents, breadcrumbs = [], headerBorder =
|
||||
{ enabled: selectedOrganization && !selectedOrganization.subscription_id }
|
||||
)
|
||||
|
||||
const projectHasNoLimits = subscription?.usage_billing_enabled === false
|
||||
const projectHasNoLimits = subscription?.usage_billing_enabled === true
|
||||
|
||||
const showOverUsageBadge =
|
||||
useFlag('overusageBadge') &&
|
||||
|
||||
@@ -2,7 +2,7 @@ import { toJS } from 'mobx'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
|
||||
import { useFlag, useSelectedOrganization, useStore } from 'hooks'
|
||||
import { useSelectedOrganization, useStore } from 'hooks'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import { Button, Dropdown, IconPlus } from 'ui'
|
||||
|
||||
@@ -13,8 +13,6 @@ const OrgDropdown = () => {
|
||||
const { data: organizations } = useOrganizationsQuery()
|
||||
const selectedOrganization = useSelectedOrganization()
|
||||
|
||||
const orgCreationV2 = useFlag('orgcreationv2')
|
||||
|
||||
return IS_PLATFORM ? (
|
||||
<Dropdown
|
||||
side="bottom"
|
||||
@@ -54,10 +52,7 @@ const OrgDropdown = () => {
|
||||
})}
|
||||
<Dropdown.Separator />
|
||||
|
||||
<Dropdown.Item
|
||||
icon={<IconPlus size="tiny" />}
|
||||
onClick={() => router.push(orgCreationV2 ? `/new-with-subscription` : `/new`)}
|
||||
>
|
||||
<Dropdown.Item icon={<IconPlus size="tiny" />} onClick={() => router.push(`/new`)}>
|
||||
New organization
|
||||
</Dropdown.Item>
|
||||
</>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import { useFlag } from 'hooks'
|
||||
import { Button, Dropdown, IconPlus } from 'ui'
|
||||
import { EMPTY_ARR } from 'lib/void'
|
||||
|
||||
const OrganizationDropdown = ({ organizations = EMPTY_ARR }) => {
|
||||
const router = useRouter()
|
||||
const orgCreationV2 = useFlag('orgcreationv2')
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
@@ -29,7 +27,7 @@ const OrganizationDropdown = ({ organizations = EMPTY_ARR }) => {
|
||||
<Dropdown.Separator />
|
||||
<Dropdown.Item
|
||||
icon={<IconPlus size="tiny" />}
|
||||
onClick={() => router.push(orgCreationV2 ? `/new-with-subscription` : `/new`)}
|
||||
onClick={() => router.push(`/new`)}
|
||||
>
|
||||
New organization
|
||||
</Dropdown.Item>
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { useParams } from 'common'
|
||||
import { compact, get, isEmpty, uniqBy } from 'lodash'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
|
||||
import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
|
||||
import { useStore } from 'hooks'
|
||||
import { DEFAULT_PROJECT_API_SERVICE_ID, IS_PLATFORM } from 'lib/constants'
|
||||
import { copyToClipboard } from 'lib/helpers'
|
||||
import { useStorageStore } from 'localStores/storageExplorer/StorageExplorerStore'
|
||||
import { STORAGE_ROW_TYPES } from '../Storage.constants'
|
||||
import ConfirmDeleteModal from './ConfirmDeleteModal'
|
||||
@@ -12,11 +17,6 @@ import FileExplorerHeader from './FileExplorerHeader'
|
||||
import FileExplorerHeaderSelection from './FileExplorerHeaderSelection'
|
||||
import MoveItemsModal from './MoveItemsModal'
|
||||
import PreviewPane from './PreviewPane'
|
||||
import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
|
||||
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
|
||||
import { DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants'
|
||||
import { copyToClipboard } from 'lib/helpers'
|
||||
import { useStore } from 'hooks'
|
||||
|
||||
const StorageExplorer = observer(({ bucket }) => {
|
||||
const storageExplorerStore = useStorageStore()
|
||||
@@ -51,7 +51,10 @@ const StorageExplorer = observer(({ bucket }) => {
|
||||
|
||||
const { ui } = useStore()
|
||||
const { ref } = useParams()
|
||||
const { data: customDomainData } = useCustomDomainsQuery({ projectRef: ref })
|
||||
const { data: customDomainData } = useCustomDomainsQuery(
|
||||
{ projectRef: ref },
|
||||
{ enabled: IS_PLATFORM }
|
||||
)
|
||||
const { data: projectSettings } = useProjectSettingsQuery({ projectRef: ref })
|
||||
const apiService = (projectSettings?.services ?? []).find(
|
||||
(x) => x.app.id == DEFAULT_PROJECT_API_SERVICE_ID
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Modal, Button, Form } from 'ui'
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
// [Joshen] As of 280222, let's just use THIS component as the one and only confirmation modal
|
||||
// deprecate all others, since it uses the Form component from the UI library as well
|
||||
// [Joshen] As of 280222, let's just use ConfirmationModal as the one and only confirmation modal (Deprecate this)
|
||||
|
||||
const ConfirmModal = ({
|
||||
visible = false,
|
||||
|
||||
@@ -97,6 +97,10 @@ export interface paths {
|
||||
/** Restore project backup */
|
||||
post: operations["BackupsController_restoreBackup"];
|
||||
};
|
||||
"/platform/database/{ref}/backups/restore-physical": {
|
||||
/** Restore project with a physical backup */
|
||||
post: operations["BackupsController_restorePhysicalBackup"];
|
||||
};
|
||||
"/platform/database/{ref}/backups/pitr": {
|
||||
/** Restore project to a previous point in time */
|
||||
post: operations["BackupsController_restorePointInTimeBackup"];
|
||||
@@ -929,6 +933,10 @@ export interface paths {
|
||||
/** Restore project backup */
|
||||
post: operations["BackupsController_restoreBackup"];
|
||||
};
|
||||
"/v0/database/{ref}/backups/restore-physical": {
|
||||
/** Restore project with a physical backup */
|
||||
post: operations["BackupsController_restorePhysicalBackup"];
|
||||
};
|
||||
"/v0/database/{ref}/backups/pitr": {
|
||||
/** Restore project to a previous point in time */
|
||||
post: operations["BackupsController_restorePointInTimeBackup"];
|
||||
@@ -1573,6 +1581,10 @@ export interface paths {
|
||||
/** Create an organization */
|
||||
post: operations["OrganizationsController_createOrganization"];
|
||||
};
|
||||
"/v1/organizations/{slug}/members": {
|
||||
/** List members of an organization */
|
||||
get: operations["V1OrganizationMembersController_v1ListOrganizationMembers"];
|
||||
};
|
||||
"/v1/oauth/authorize": {
|
||||
/** Authorize user through oauth */
|
||||
get: operations["OAuthController_authorize"];
|
||||
@@ -1581,6 +1593,14 @@ export interface paths {
|
||||
/** Exchange auth code for user's access and refresh token */
|
||||
post: operations["OAuthController_token"];
|
||||
};
|
||||
"/v1/snippets": {
|
||||
/** Lists SQL snippets for the logged in user */
|
||||
get: operations["SnippetsController_listSnippets"];
|
||||
};
|
||||
"/v1/snippets/{id}": {
|
||||
/** Gets a specific SQL snippet */
|
||||
get: operations["SnippetsController_getSnippet"];
|
||||
};
|
||||
}
|
||||
|
||||
export type webhooks = Record<string, never>;
|
||||
@@ -1647,12 +1667,16 @@ export interface components {
|
||||
redirectTo?: string;
|
||||
};
|
||||
ProjectResourceWarningsResponse: {
|
||||
/** @enum {string|null} */
|
||||
disk_io_exhaustion: "critical" | "warning" | null;
|
||||
/** @enum {string|null} */
|
||||
disk_space_exhaustion: "critical" | "warning" | null;
|
||||
/** @enum {string|null} */
|
||||
cpu_exhaustion: "critical" | "warning" | null;
|
||||
/** @enum {string|null} */
|
||||
memory_and_swap_exhaustion: "critical" | "warning" | null;
|
||||
project: string;
|
||||
is_readonly_mode_enabled: boolean;
|
||||
is_disk_io_budget_below_threshold: boolean;
|
||||
is_disk_space_usage_beyond_threshold: boolean;
|
||||
is_cpu_load_beyond_threshold: boolean;
|
||||
is_memory_and_swap_usage_beyond_threshold: boolean;
|
||||
};
|
||||
GetGoTrueConfigResponse: {
|
||||
SITE_URL: string;
|
||||
@@ -2064,11 +2088,9 @@ export interface components {
|
||||
};
|
||||
Backup: {
|
||||
id: number;
|
||||
data: Record<string, never>;
|
||||
isPhysicalBackup: boolean;
|
||||
project_id: number;
|
||||
status: Record<string, never>;
|
||||
s3_path: string;
|
||||
s3_bucket: string;
|
||||
inserted_at: string;
|
||||
};
|
||||
BackupsResponse: {
|
||||
@@ -2076,6 +2098,7 @@ export interface components {
|
||||
tierKey: string;
|
||||
region: string;
|
||||
walg_enabled: boolean;
|
||||
pitr_enabled: boolean;
|
||||
backups: (components["schemas"]["Backup"])[];
|
||||
physicalBackupData: {
|
||||
earliestPhysicalBackupDateUnix?: number;
|
||||
@@ -2094,14 +2117,12 @@ export interface components {
|
||||
DownloadBackupResponse: {
|
||||
fileUrl: string;
|
||||
};
|
||||
RestoreBackupBody: {
|
||||
RestoreLogicalBackupBody: {
|
||||
id: number;
|
||||
data: Record<string, never>;
|
||||
inserted_at: string;
|
||||
project_id: number;
|
||||
s3_bucket: string;
|
||||
s3_path: string;
|
||||
status: string;
|
||||
};
|
||||
RestorePhysicalBackupBody: {
|
||||
id: number;
|
||||
recovery_time_target: string;
|
||||
};
|
||||
PointInTimeRestoreBody: {
|
||||
recovery_time_target_unix: number;
|
||||
@@ -4289,6 +4310,12 @@ export interface components {
|
||||
entrypoint_path?: string;
|
||||
import_map_path?: string;
|
||||
};
|
||||
V1OrganizationMemberResponse: {
|
||||
user_id: string;
|
||||
user_name: string;
|
||||
email?: string;
|
||||
role_name: string;
|
||||
};
|
||||
OAuthTokenBody: {
|
||||
/** @enum {string} */
|
||||
grant_type: "authorization_code" | "refresh_token";
|
||||
@@ -4306,6 +4333,51 @@ export interface components {
|
||||
refresh_token: string;
|
||||
expires_in: number;
|
||||
};
|
||||
SnippetProject: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
SnippetUser: {
|
||||
id: number;
|
||||
username: string;
|
||||
};
|
||||
SnippetMeta: {
|
||||
id: string;
|
||||
inserted_at: string;
|
||||
updated_at: string;
|
||||
/** @enum {string} */
|
||||
type: "sql";
|
||||
/** @enum {string} */
|
||||
visibility: "user" | "project" | "org" | "public";
|
||||
name: string;
|
||||
description: string | null;
|
||||
project: components["schemas"]["SnippetProject"];
|
||||
owner: components["schemas"]["SnippetUser"];
|
||||
updated_by: components["schemas"]["SnippetUser"];
|
||||
};
|
||||
SnippetList: {
|
||||
data: (components["schemas"]["SnippetMeta"])[];
|
||||
};
|
||||
SnippetContent: {
|
||||
favorite: boolean;
|
||||
schema_version: string;
|
||||
sql: string;
|
||||
};
|
||||
SnippetResponse: {
|
||||
id: string;
|
||||
inserted_at: string;
|
||||
updated_at: string;
|
||||
/** @enum {string} */
|
||||
type: "sql";
|
||||
/** @enum {string} */
|
||||
visibility: "user" | "project" | "org" | "public";
|
||||
name: string;
|
||||
description: string | null;
|
||||
project: components["schemas"]["SnippetProject"];
|
||||
owner: components["schemas"]["SnippetUser"];
|
||||
updated_by: components["schemas"]["SnippetUser"];
|
||||
content: components["schemas"]["SnippetContent"];
|
||||
};
|
||||
};
|
||||
responses: never;
|
||||
parameters: never;
|
||||
@@ -4735,7 +4807,7 @@ export interface operations {
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["RestoreBackupBody"];
|
||||
"application/json": components["schemas"]["RestoreLogicalBackupBody"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
@@ -4748,6 +4820,29 @@ export interface operations {
|
||||
500: never;
|
||||
};
|
||||
};
|
||||
/** Restore project with a physical backup */
|
||||
BackupsController_restorePhysicalBackup: {
|
||||
parameters: {
|
||||
path: {
|
||||
/** @description Project ref */
|
||||
ref: string;
|
||||
};
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["RestorePhysicalBackupBody"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
201: {
|
||||
content: {
|
||||
"application/json": Record<string, never>;
|
||||
};
|
||||
};
|
||||
/** @description Failed to restore project with physical backup */
|
||||
500: never;
|
||||
};
|
||||
};
|
||||
/** Restore project to a previous point in time */
|
||||
BackupsController_restorePointInTimeBackup: {
|
||||
parameters: {
|
||||
@@ -10298,6 +10393,21 @@ export interface operations {
|
||||
500: never;
|
||||
};
|
||||
};
|
||||
/** List members of an organization */
|
||||
V1OrganizationMembersController_v1ListOrganizationMembers: {
|
||||
parameters: {
|
||||
path: {
|
||||
slug: string;
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": (components["schemas"]["V1OrganizationMemberResponse"])[];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
/** Authorize user through oauth */
|
||||
OAuthController_authorize: {
|
||||
parameters: {
|
||||
@@ -10305,7 +10415,7 @@ export interface operations {
|
||||
client_id: string;
|
||||
response_type: "code" | "token" | "id_token token";
|
||||
redirect_uri: string;
|
||||
scope: string;
|
||||
scope?: string;
|
||||
state?: string;
|
||||
response_mode?: string;
|
||||
code_challenge?: string;
|
||||
@@ -10331,4 +10441,38 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
/** Lists SQL snippets for the logged in user */
|
||||
SnippetsController_listSnippets: {
|
||||
parameters: {
|
||||
query?: {
|
||||
project_ref?: string;
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["SnippetList"];
|
||||
};
|
||||
};
|
||||
/** @description Failed to list user's SQL snippets */
|
||||
500: never;
|
||||
};
|
||||
};
|
||||
/** Gets a specific SQL snippet */
|
||||
SnippetsController_getSnippet: {
|
||||
parameters: {
|
||||
path: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["SnippetResponse"];
|
||||
};
|
||||
};
|
||||
/** @description Failed to retrieve SQL snippet */
|
||||
500: never;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -40,21 +40,9 @@ export const useProjectReadOnlyQuery = (
|
||||
return data.result[0]?.default_transaction_read_only === 'on'
|
||||
},
|
||||
enabled: typeof projectRef !== 'undefined' && typeof connectionString !== 'undefined',
|
||||
retry: (failureCount) => {
|
||||
return failureCount < 3
|
||||
},
|
||||
...options,
|
||||
}
|
||||
)
|
||||
|
||||
export const useProjectReadOnlyPrefetch = () => {
|
||||
const prefetch = useExecuteSqlPrefetch()
|
||||
|
||||
return useCallback(
|
||||
({ projectRef, connectionString }: ProjectReadOnlyVariables) =>
|
||||
prefetch({
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getProjectReadOnlySql(),
|
||||
queryKey: ['project-read-only'],
|
||||
}),
|
||||
[prefetch]
|
||||
)
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export type Backup = components['schemas']['Backup']
|
||||
export async function restoreFromBackup({ ref, backup }: BackupRestoreVariables) {
|
||||
const { data, error } = await post('/platform/database/{ref}/backups/restore', {
|
||||
params: { path: { ref } },
|
||||
body: { ...backup, status: backup.status as unknown as string },
|
||||
body: { id: backup.id },
|
||||
})
|
||||
if (error) throw error
|
||||
return data
|
||||
|
||||
@@ -21,7 +21,7 @@ export type PoolingConfiguration = {
|
||||
inserted_at: string
|
||||
max_client_conn: number | null
|
||||
pgbouncer_enabled: boolean
|
||||
supavisor_enabled?: boolean
|
||||
supavisor_enabled: boolean
|
||||
pgbouncer_status: string
|
||||
pool_mode: string
|
||||
connectionString: string
|
||||
|
||||