Merge branch 'master' of github.com:supabase/supabase

This commit is contained in:
Terry Sutton committed 2023-01-27 15:14:41 -03:30
commit a847b5e2c1
99 files changed
+2153 -511

No files matched your search

+20
View File
@@ -0,0 +1,20 @@
name: reviewdog
on:
pull_request:
types: [opened, synchronize, reopened, edited]
jobs:
misspell:
name: runner / misspell
runs-on: ubuntu-latest
steps:
- name: Check out code.
uses: actions/checkout@v1
- name: misspell
uses: reviewdog/action-misspell@v1
with:
github_token: ${{ secrets.github_token }}
locale: "US"
reporter: github-pr-review
level: error
exclude: "*.css"
@@ -0,0 +1,108 @@
If you upload additional profile photos, they'll accumulate
in the `avatars` bucket because of their random names with only the latest being referenced
from `public.profiles` and the older versions getting orphaned.
To automatically remove obsolete storage objects, extend the database
triggers. Note that it is not sufficient to delete the objects from the
`storage.objects` table because that would orphan and leak the actual storage objects in
the S3 backend. Instead, invoke the storage API within Postgres via the `http` extension.
Enable the [http extension for the `extensions` schema](https://app.supabase.com/project/_/database/extensions) in the Dashboard.
Then, define the following SQL functions in the SQL Editor to delete
storage objects via the API:
```SQL
create or replace function delete_storage_object(bucket text, object text, out status int, out content varchar)
returns record
language 'plpgsql'
security definer
as $$
declare
project_url varchar := '<YOURPROJECTURL>';
service_role_key varchar := '<YOURSERVICEROLEKEY>'; -- full access needed
url varchar := project_url||'/storage/v1/object/'||bucket||'/'||object;
begin
select
into status, content
result.status::int, result.content::varchar
FROM extensions.http((
'DELETE',
url,
ARRAY[extensions.http_header('authorization','Bearer '||service_role_key)],
NULL,
NULL)::extensions.http_request) as result;
end;
$$;
create or replace function delete_avatar(avatar_url text, out status int, out content varchar)
returns record
language 'plpgsql'
security definer
as $$
begin
select
into status, content
result.status, result.content
from public.delete_storage_object('avatars', avatar_url) as result;
end;
$$;
```
Next, add a trigger that removes any obsolete avatar whenever the
profile is updated or deleted:
```SQL
create or replace function delete_old_avatar()
returns trigger
language 'plpgsql'
security definer
as $$
declare
status int;
content varchar;
begin
if coalesce(old.avatar_url, '') <> ''
and (tg_op = 'DELETE' or (old.avatar_url <> new.avatar_url)) then
select
into status, content
result.status, result.content
from public.delete_avatar(old.avatar_url) as result;
if status <> 200 then
raise warning 'Could not delete avatar: % %', status, content;
end if;
end if;
if tg_op = 'DELETE' then
return old;
end if;
return new;
end;
$$;
create trigger before_profile_changes
before update of avatar_url or delete on public.profiles
for each row execute function public.delete_old_avatar();
```
Finally, delete the `public.profile` row before a user is deleted.
If this step is omitted, you won't be able to delete users without
first manually deleting their avatar image.
```SQL
create or replace function delete_old_profile()
returns trigger
language 'plpgsql'
security definer
as $$
begin
delete from public.profiles where id = old.id;
return old;
end;
$$;
create trigger before_delete_user
before delete on auth.users
for each row execute function public.delete_old_profile();
```
+2
View File
@@ -20,6 +20,7 @@ import QuickstartIntro from './MDX/quickstart_intro.mdx'
import ProjectSetup from './MDX/project_setup.mdx'
import SocialProviderSetup from './MDX/social_provider_setup.mdx'
import SocialProviderSettingsSupabase from './MDX/social_provider_settings_supabase.mdx'
import StorageManagement from './MDX/storage_management.mdx'
import { Mermaid } from 'mdx-mermaid/lib/Mermaid'
import InlineCodeTag from './CustomHTMLElements/InlineCode'
import React from 'react'
@@ -49,6 +50,7 @@ const components = {
ProjectSetup,
SocialProviderSetup,
SocialProviderSettingsSupabase,
StorageManagement,
Mermaid,
Extensions,
Alert: (props: any) => (
+7
View File
@@ -468,6 +468,13 @@
"installed_version": "1.1",
"comment": "generate universally unique identifiers (UUIDs)"
},
{
"name": "vector",
"schema": "extensions",
"default_version": "0.4.0",
"installed_version": null,
"comment": "vector data type with similarity search"
},
{
"name": "xml2",
"schema": null,
+1 -1
View File
@@ -217,7 +217,7 @@ import { createClient, useQuery } from 'urql'
// Prepare API key and Authorization header
const headers = {
apikey: <SUPABASE_ANON_KEY>,
authorization: `Bearer ${<SUPABASE_ANON_KEY}`>
authorization: `Bearer ${<SUPABASE_ANON_KEY>}`,
}
// Create GraphQL client
@@ -6,6 +6,15 @@ export const meta = {
description: 'Using Import Maps.',
}
<div class="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/ILr3cneZuFk"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
Since [Supabase CLI](https://supabase.com/docs/guides/resources/supabase-cli) version `1.33.0` [import maps](https://deno.land/manual@v1.29.2/node/import_maps) can be used with Supabase Edge Functions.
[Import maps](https://github.com/WICG/import-maps#the-import-map) is a web-platform standard that allows you to use bare specifiers with Deno without having to install the Node.js package locally.
+1 -1
View File
@@ -25,7 +25,7 @@ By default, Edge Functions have access to these secrets:
Let's create a local file for storing our secrets, and inside it we can store a secret `MY_NAME`:
```jsx
```bash
echo "MY_NAME=Yoda" >> ./supabase/.env.local
```
@@ -530,6 +530,10 @@ export class AccountComponent implements OnInit {
}
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -500,6 +500,10 @@ Now you will need to run the prebuild command to get the application working on
expo prebuild
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -793,6 +793,10 @@ class _AccountPageState extends State<AccountPage> {
}
```
### Storage management
<StorageManagement />
Congratulations, that is it! You have now built a fully functional user management app using Flutter and Supabase!
## See also
@@ -522,6 +522,10 @@ template: `
`
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -495,6 +495,10 @@ return (
<Avatar url={profile.avatar_url} onUpload={updateProfile}></Avatar>
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -555,6 +555,10 @@ export default defineComponent({
</script>
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -718,6 +718,10 @@ return (
)
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -420,6 +420,10 @@ And then we can add the widget to the Account page:
That is it! You should now be able to upload a profile photo to Supabase Storage.
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -397,6 +397,10 @@ return (
)
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -771,6 +771,10 @@ return (
)
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -415,6 +415,10 @@ return (
)
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -368,6 +368,10 @@ And then we can add the widget to the Account page:
</form>
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -434,6 +434,10 @@ And then we can add the widget to the Account page:
</form>
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -379,6 +379,10 @@ And then we can add the widget to the Account page:
</template>
```
### Storage management
<StorageManagement />
## Next steps
At this stage you have a fully functional application!
@@ -6,6 +6,8 @@ image: launch-week-6/crdt/og-pg-crdt.png
thumb: launch-week-6/crdt/og-pg-crdt.png
tags:
- launch-week
- postgres
- planetpg
date: '2022-12-10'
toc_depth: 2
---
+1 -1
View File
@@ -36,7 +36,7 @@
"next-seo": "^4.20.0",
"next-transpile-modules": "^9.0.0",
"nodemailer": "^6.7.3",
"octokit": "^2.0.10",
"octokit": "^2.0.14",
"parse-numeric-range": "^1.3.0",
"react": "^17.0.2",
"react-copy-to-clipboard": "^5.0.2",
+1 -3
View File
@@ -9,7 +9,7 @@ version: "3.8"
services:
studio:
container_name: supabase-studio
image: supabase/studio:20221214-4eecc99
image: supabase/studio:20230127-123d704
restart: unless-stopped
healthcheck:
test: [ "CMD", "node", "-e", "require('http').get('http://localhost:3000/api/profile', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})" ]
@@ -27,8 +27,6 @@ services:
SUPABASE_URL: http://kong:8000
SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
# Kept for backwards compatibility with studio:0.22.08
SUPABASE_REST_URL: ${API_EXTERNAL_URL}/rest/v1/
SUPABASE_ANON_KEY: ${ANON_KEY}
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
@@ -6,7 +6,7 @@ router
.get('/oak-server', (context) => {
context.response.body = 'This is an example Oak server running on Edge Functions!'
})
.post('/oak-server/greet', (context) => {
.post('/oak-server/greet', async (context) => {
// Note: request body will be streamed to the function as chunks, set limit to 0 to fully read it.
const result = context.request.body({ type: 'json', limit: 0 })
const body = await result.value
+1766 -302
View File
File diff suppressed because it is too large. Load diff
+3
View File
@@ -62,5 +62,8 @@
"mdx-mermaid": "^1.3.2",
"mermaid": "^9.2.2",
"mini-svg-data-uri": "^1.4.4"
},
"overrides": {
"pgsql-parser": "13.4.0"
}
}
-22
View File
@@ -17,7 +17,6 @@ info:
functions:
- id: initializing
$ref: '@supabase/supabase-js.index.SupabaseClient.constructor'
description: |
You can initialize a new Supabase client using the `create_client()` method.
@@ -50,7 +49,6 @@ functions:
- id: sign-up
title: 'sign_up()'
$ref: '@supabase/gotrue-js.GoTrueClient.signUp'
notes: |
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://app.supabase.com/project/_/auth/providers).
- **Confirm email** determines if users need to confirm their email address after signing up.
@@ -88,7 +86,6 @@ functions:
```
- id: sign-out
title: 'sign_out()'
$ref: '@supabase/gotrue-js.GoTrueClient.signOut'
notes: |
- In order to use the `signOut()` method, the user needs to be signed in first.
examples:
@@ -100,7 +97,6 @@ functions:
```
- id: verify-otp
title: 'verify_otp()'
$ref: '@supabase/gotrue-js.GoTrueClient.verifyOtp'
notes: |
- The `verifyOtp` method takes in different verification types. If a phone number is used, the type can either be `sms` or `phone_change`. If an email address is used, the type can be one of the following: `signup`, `magiclink`, `recovery`, `invite` or `email_change`.
- The verification type used should be determined based on the corresponding auth method called before `verifyOtp` to sign up / sign-in a user.
@@ -114,7 +110,6 @@ functions:
- id: get-session
title: 'get_session_from_url'
$ref: '@supabase/gotrue-js.GoTrueClient.getSession'
examples:
- id: get-session
name: Get the session data from URL
@@ -125,7 +120,6 @@ functions:
- id: set-session
title: 'set_session()'
$ref: '@supabase/gotrue-js.GoTrueClient.setSession'
notes: |
- `setSession()` takes in a refresh token and uses it to get a new session.
- The refresh token can only be used once to obtain a new session.
@@ -142,7 +136,6 @@ functions:
```
- id: refresh-session
title: 'refresh_ession()'
$ref: '@supabase/gotrue-js.GoTrueClient.refreshSession'
notes: |
- This method will refresh the session whether the current one is expired or not.
- Both examples destructure `user` and `session` from `data`. This is not required; so `const { data, error } =` is also valid.
@@ -155,7 +148,6 @@ functions:
```
- id: select
title: 'Fetch data: select()'
$ref: '@supabase/postgrest-js.PostgrestQueryBuilder.select'
notes: |
- By default, Supabase projects return a maximum of 1,000 rows. This setting can be changed in your project's [API settings](https://app.supabase.com/project/_/settings/api). It's recommended that you keep it low to limit the payload size of accidental or malicious requests.
- `apikey` is a reserved keyword if you're using the [Supabase Platform](/docs/guides/platform) and [should be avoided as a column name](https://github.com/supabase/supabase/issues/5465).
@@ -181,7 +173,6 @@ functions:
title: 'invoke()'
description: |
Invoke a Supabase Function.
$ref: '@supabase/functions-js.F'
notes: |
- Requires an Authorization header.
- When you pass in a body to your function, we automatically attach the Content-Type header for `Blob`, `ArrayBuffer`, `File`, `FormData` and `String`. If it doesn't match any of these types we assume the payload is `json`, serialise it and attach the `Content-Type` header as `application/json`. You can override this behaviour by passing in a `Content-Type` header of your own.
@@ -204,7 +195,6 @@ functions:
- id: list-buckets
title: 'list_buckets()'
$ref: '@supabase/storage-js.packages/StorageBucketApi.default.listBuckets'
notes: |
- RLS policy permissions required:
- `buckets` table permissions: `select`
@@ -220,7 +210,6 @@ functions:
- id: get-bucket
title: 'get_bucket()'
$ref: '@supabase/storage-js.packages/StorageBucketApi.default.getBucket'
notes: |
- RLS policy permissions required:
- `buckets` table permissions: `select`
@@ -236,7 +225,6 @@ functions:
- id: create-bucket
title: 'create_bucket()'
$ref: '@supabase/storage-js.packages/StorageBucketApi.default.createBucket'
notes: |
- RLS policy permissions required:
- `buckets` table permissions: `insert`
@@ -252,7 +240,6 @@ functions:
- id: empty-bucket
title: 'empty_bucket()'
$ref: '@supabase/storage-js.packages/StorageBucketApi.default.emptyBucket'
notes: |
- RLS policy permissions required:
- `buckets` table permissions: `select`
@@ -267,7 +254,6 @@ functions:
```
- id: delete-bucket
title: 'delete_bucket()'
$ref: '@supabase/storage-js.packages/StorageBucketApi.default.deleteBucket'
notes: |
- RLS policy permissions required:
- `buckets` table permissions: `select` and `delete`
@@ -283,7 +269,6 @@ functions:
- id: from-upload
title: 'from_.upload()'
$ref: '@supabase/storage-js.packages/StorageFileApi.default.upload'
notes: |
- RLS policy permissions required:
- `buckets` table permissions: none
@@ -299,7 +284,6 @@ functions:
```
- id: from-move
title: 'from_.move()'
$ref: '@supabase/storage-js.packages/StorageFileApi.default.move'
notes: |
- RLS policy permissions required:
- `buckets` table permissions: none
@@ -315,7 +299,6 @@ functions:
- id: from-create-signed-url
title: 'from_.create_signed_url()'
$ref: '@supabase/storage-js.packages/StorageFileApi.default.createSignedUrl'
notes: |
- RLS policy permissions required:
- `buckets` table permissions: none
@@ -331,7 +314,6 @@ functions:
- id: from-get-public-url
title: 'from_.get_public_url()'
$ref: '@supabase/storage-js.packages/StorageFileApi.default.getPublicUrl'
notes: |
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [app.supabase.com](https://app.supabase.com), clicking the overflow menu on a bucket and choosing "Make public"
- RLS policy permissions required:
@@ -348,7 +330,6 @@ functions:
- id: from-download
title: 'from_.download()'
$ref: '@supabase/storage-js.packages/StorageFileApi.default.download'
notes: |
- RLS policy permissions required:
- `buckets` table permissions: none
@@ -366,7 +347,6 @@ functions:
- id: from-remove
title: 'from_.remove()'
$ref: '@supabase/storage-js.packages/StorageFileApi.default.remove'
notes: |
- RLS policy permissions required:
- `buckets` table permissions: none
@@ -381,7 +361,6 @@ functions:
```
- id: from-list
title: 'from_.list()'
$ref: '@supabase/storage-js.packages/StorageFileApi.default.list'
notes: |
- RLS policy permissions required:
- `buckets` table permissions: none
@@ -396,6 +375,5 @@ functions:
```
- id: subscribe
title: on().subscribe()
$ref: '@supabase/realtime-js.RealtimeChannel.on'
notes: |
- We are implementing this feature at the moment. If you have queries feel free to open an issue on the [realtime-py](https://github.com/supabase-community) repository.
+1 -3
View File
@@ -6,13 +6,11 @@ DEFAULT_PROJECT_NAME=Default Project
SUPABASE_URL=http://localhost:8000
SUPABASE_PUBLIC_URL=http://localhost:8000
NEXT_PUBLIC_SUPABASE_URL=$SUPABASE_URL
NEXT_PUBLIC_SUPABASE_PUBLIC_URL=$SUPABASE_PUBLIC_URL
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE
SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q
# TODO: evaluate these variables at runtime instead
# https://nextjs.org/docs/basic-features/environment-variables#loading-environment-variables
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_GOTRUE_URL=$NEXT_PUBLIC_SUPABASE_PUBLIC_URL/auth/v1
NEXT_PUBLIC_GOTRUE_URL=$SUPABASE_PUBLIC_URL/auth/v1
NEXT_PUBLIC_HCAPTCHA_SITE_KEY=10000000-ffff-ffff-ffff-000000000001
@@ -1,4 +1,4 @@
import { Tabs } from 'ui'
import { Button, IconExternalLink, Tabs } from 'ui'
import { observer } from 'mobx-react-lite'
import { TEMPLATES_SCHEMAS } from 'stores/authConfig/schema'
@@ -8,10 +8,20 @@ import TemplateEditor from './TemplateEditor'
const EmailTemplates = observer(() => {
return (
<div>
<FormHeader
title="Email Templates"
description="Customize the emails that will be sent out to your users."
/>
<div className='flex justify-between items-center'>
<FormHeader
title="Email Templates"
description="Customize the emails that will be sent out to your users."
/>
<Button type="link" icon={<IconExternalLink size={14} strokeWidth={1.5} />}>
<a
target="_blank"
href="https://supabase.com/docs/guides/auth/auth-email-templates"
>
Email Templates Documentation
</a>
</Button>
</div>
<FormPanel>
<Tabs
scrollable
@@ -5,7 +5,7 @@ import { useRouter } from 'next/router'
import { observer } from 'mobx-react-lite'
import { useParams, useStore } from 'hooks'
import { PolicyEditorModal, PolicyTableRow } from 'components/interfaces/Auth/Policies'
import { PostgresRole, PostgresTable } from '@supabase/postgres-meta'
import type { PostgresRole, PostgresTable } from '@supabase/postgres-meta'
import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog'
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
@@ -1,4 +1,4 @@
import { PostgresPolicy } from '@supabase/postgres-meta'
import type { PostgresPolicy } from '@supabase/postgres-meta'
import { has, isEmpty, isEqual } from 'lodash'
import {
PolicyFormField,
@@ -1,5 +1,5 @@
import { FC } from 'react'
import { PostgresRole } from '@supabase/postgres-meta'
import type { PostgresRole } from '@supabase/postgres-meta'
import MultiSelect from 'components/ui/MultiSelect'
@@ -6,7 +6,7 @@ import PolicyDefinition from './PolicyDefinition'
import PolicyAllowedOperation from './PolicyAllowedOperation'
import PolicyRoles from './PolicyRoles'
import PolicyEditorFooter from './PolicyEditorFooter'
import { PostgresRole } from '@supabase/postgres-meta'
import type { PostgresRole } from '@supabase/postgres-meta'
// Exposed for StoragePoliciesEditor.js
export { PolicyName, PolicyRoles }
@@ -1,7 +1,7 @@
import { Modal } from 'ui'
import { FC, useState, useEffect } from 'react'
import { isEmpty } from 'lodash'
import { PostgresRole } from '@supabase/postgres-meta'
import type { PostgresRole } from '@supabase/postgres-meta'
import { useStore } from 'hooks'
import PolicyEditorModalTitle from './PolicyEditorModalTitle'
@@ -1,7 +1,7 @@
import { FC } from 'react'
import * as Tooltip from '@radix-ui/react-tooltip'
import { Button, Dropdown, IconEdit, IconTrash, IconMoreVertical } from 'ui'
import { PostgresPolicy } from '@supabase/postgres-meta'
import type { PostgresPolicy } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { checkPermissions } from 'hooks'
@@ -3,7 +3,7 @@ import { FC } from 'react'
import { useRouter } from 'next/router'
import * as Tooltip from '@radix-ui/react-tooltip'
import { Badge, Button } from 'ui'
import { PostgresTable } from '@supabase/postgres-meta'
import type { PostgresTable } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { checkPermissions } from 'hooks'
@@ -1,5 +1,5 @@
import { FC } from 'react'
import { PostgresTable, PostgresPolicy } from '@supabase/postgres-meta'
import type { PostgresTable, PostgresPolicy } from '@supabase/postgres-meta'
import { observer } from 'mobx-react-lite'
import { useStore } from 'hooks'
@@ -1,6 +1,6 @@
import { FC, useEffect, useState } from 'react'
import { Button, Input, Form, Modal, Listbox, IconPlus, IconDatabase } from 'ui'
import { PostgresExtension, PostgresSchema } from '@supabase/postgres-meta'
import type { PostgresExtension, PostgresSchema } from '@supabase/postgres-meta'
import { useStore } from 'hooks'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
@@ -1,7 +1,7 @@
import { FC, useState } from 'react'
import { observer } from 'mobx-react-lite'
import { Badge, Toggle } from 'ui'
import { PostgresPublication, PostgresTable } from '@supabase/postgres-meta'
import type { PostgresPublication, PostgresTable } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { checkPermissions, useStore } from 'hooks'
@@ -1,7 +1,7 @@
import { FC, useState } from 'react'
import { observer } from 'mobx-react-lite'
import { Button, Input, IconChevronLeft, IconSearch, IconAlertCircle } from 'ui'
import { PostgresPublication } from '@supabase/postgres-meta'
import type { PostgresPublication } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { checkPermissions, useStore } from 'hooks'
@@ -5,7 +5,7 @@ import { Input, Button, IconSearch, IconPlus, IconChevronLeft, IconEdit3, IconTr
import { useStore } from 'hooks'
import Table from 'components/to-be-cleaned/Table'
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
import { PostgresTable } from '@supabase/postgres-meta'
import type { PostgresTable } from '@supabase/postgres-meta'
interface Props {
selectedTable: PostgresTable
@@ -25,11 +25,11 @@ const ColumnList: FC<Props> = ({
const { meta } = useStore()
const [filterString, setFilterString] = useState<string>('')
const columns =
filterString.length === 0
(filterString.length === 0
? selectedTable.columns
: selectedTable.columns.filter((column: any) => column.name.includes(filterString))
: selectedTable.columns?.filter((column: any) => column.name.includes(filterString))) ?? []
const isLocked = meta.excludedSchemas.includes(selectedTable?.schema ?? '')
const isLocked = meta.excludedSchemas.includes(selectedTable.schema ?? '')
return (
<>
@@ -18,7 +18,7 @@ import { partition } from 'lodash'
import { useStore } from 'hooks'
import Table from 'components/to-be-cleaned/Table'
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
import { PostgresTable, PostgresSchema } from '@supabase/postgres-meta'
import type { PostgresTable, PostgresSchema } from '@supabase/postgres-meta'
interface Props {
selectedSchema: string
@@ -1,6 +1,6 @@
import { useRouter } from 'next/router'
import { Button, IconTerminal, IconMaximize2, IconMinimize2, IconBookOpen, IconCode } from 'ui'
import { useProjectSettings } from 'hooks'
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
import { useAccessTokens } from 'hooks/queries/useAccessTokens'
import { Commands } from './Functions.types'
import CommandRender from 'components/interfaces/Functions/CommandRender'
@@ -18,17 +18,17 @@ const TerminalInstructions: FC<Props> = ({ closable = false }) => {
const { ui } = useStore()
const { tokens } = useAccessTokens()
const { services } = useProjectSettings(ref as string | undefined)
const { data: settings } = useProjectSettingsQuery({ projectRef: ref as string })
const [showInstructions, setShowInstructions] = useState(!closable)
// Get the API service
const API_SERVICE_ID = 1
const apiService = (services ?? []).find((x: any) => x.app.id == API_SERVICE_ID)
const apiKeys = apiService?.service_api_keys ?? []
const anonKey = apiKeys.find((x: any) => x.name === 'anon key')?.api_key
const apiService = settings?.autoApiService
const anonKey = apiService?.service_api_keys.find((x: any) => x.name === 'anon key')
? apiService.defaultApiKey
: undefined
const endpoint = apiService?.app_config.endpoint ?? ''
const endpoint = apiService?.endpoint ?? ''
const endpointSections = endpoint.split('.')
const functionsEndpoint = [
...endpointSections.slice(0, 1),
@@ -5,9 +5,8 @@ import { Input, IconLoader, IconAlertCircle } from 'ui'
import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { checkPermissions, useJwtSecretUpdateStatus, useProjectSettings } from 'hooks'
import { DEFAULT_PROJECT_API_SERVICE_ID, IS_PLATFORM } from 'lib/constants'
import { PROJECT_ENDPOINT_PROTOCOL } from 'pages/api/constants'
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
import { checkPermissions, useJwtSecretUpdateStatus } from 'hooks'
import Snippets from 'components/to-be-cleaned/Docs/Snippets'
import Panel from 'components/ui/Panel'
import SimpleCodeBlock from 'components/to-be-cleaned/SimpleCodeBlock'
@@ -23,10 +22,10 @@ const APIKeys = () => {
const [selectedLanguage, setSelectedLanguage] = useState(availableLanguages[0])
const {
services,
data: settings,
isError: isProjectSettingsError,
isLoading: isProjectSettingsLoading,
} = useProjectSettings(ref as string | undefined)
} = useProjectSettingsQuery({ projectRef: ref as string })
const {
jwtSecretUpdateStatus,
@@ -37,8 +36,7 @@ const APIKeys = () => {
const canReadAPIKeys = checkPermissions(PermissionAction.READ, 'service_api_keys')
// Get the API service
const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_PROJECT_API_SERVICE_ID)
const apiConfig = apiService?.app_config ?? {}
const apiService = settings?.autoApiService
const apiKeys = apiService?.service_api_keys ?? []
// API keys should not be empty. However it can be populated with a delay on project creation
@@ -46,7 +44,7 @@ const APIKeys = () => {
const isNotUpdatingJwtSecret =
jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated
const apiUrl = `${IS_PLATFORM ? 'https' : PROJECT_ENDPOINT_PROTOCOL}://${apiConfig.endpoint}`
const apiUrl = `${apiService?.protocol ?? 'https'}://${apiService?.endpoint ?? '-'}`
const anonKey = apiKeys.find((key: any) => key.tags === 'anon')
const clientInitSnippet: any = Snippets.init(apiUrl)
@@ -111,7 +109,7 @@ const APIKeys = () => {
<div className="space-y-2">
<p className="text-sm">API Key</p>
<div className="flex items-center space-x-1 -ml-1">
{anonKey.tags?.split(',').map((x: any, i: number) => (
{anonKey?.tags?.split(',').map((x: any, i: number) => (
<code key={`${x}${i}`} className="text-xs">
{x}
</code>
@@ -121,7 +119,7 @@ const APIKeys = () => {
</div>
}
copy={canReadAPIKeys && isNotUpdatingJwtSecret}
reveal={anonKey.tags !== 'anon' && canReadAPIKeys && isNotUpdatingJwtSecret}
reveal={anonKey?.tags !== 'anon' && canReadAPIKeys && isNotUpdatingJwtSecret}
value={
!canReadAPIKeys
? 'You need additional permissions to view API keys'
@@ -129,7 +127,7 @@ const APIKeys = () => {
? 'JWT secret update failed, new API key may have issues'
: jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updating
? 'Updating JWT secret...'
: anonKey.api_key
: apiService?.defaultApiKey
}
onChange={() => {}}
descriptionText={
@@ -3,18 +3,15 @@ import { useRouter } from 'next/router'
import { JwtSecretUpdateError, JwtSecretUpdateStatus } from '@supabase/shared-types/out/events'
import { IconAlertCircle, Input } from 'ui'
import {
useStore,
useProjectSettings,
useProjectPostgrestConfig,
useJwtSecretUpdateStatus,
} from 'hooks'
import { configKeys } from 'data/config/keys'
import { useQueryClient } from '@tanstack/react-query'
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
import { useStore, useProjectPostgrestConfig, useJwtSecretUpdateStatus } from 'hooks'
import Panel from 'components/ui/Panel'
import PostgrestConfig from './PostgrestConfig'
import { DisplayApiSettings } from 'components/ui/ProjectSettings'
import { JWT_SECRET_UPDATE_ERROR_MESSAGES } from './API.constants'
import { PROJECT_ENDPOINT_PROTOCOL } from 'pages/api/constants'
import { IS_PLATFORM } from 'lib/constants'
import JWTSettings from './JWTSettings'
@@ -26,8 +23,9 @@ const ServiceList: FC<Props> = ({ projectRef }) => {
const { ui } = useStore()
const router = useRouter()
const { ref } = router.query
const queryClient = useQueryClient()
const { services, isError, mutateSettings } = useProjectSettings(ref as string | undefined)
const { data: settings, isError } = useProjectSettingsQuery({ projectRef: ref as string })
const { mutateConfig } = useProjectPostgrestConfig(ref as string | undefined)
const { jwtSecretUpdateError, jwtSecretUpdateStatus }: any = useJwtSecretUpdateStatus(ref)
@@ -41,7 +39,7 @@ const ServiceList: FC<Props> = ({ projectRef }) => {
switch (jwtSecretUpdateStatus) {
case Updated:
mutateConfig()
mutateSettings()
queryClient.invalidateQueries({ queryKey: configKeys.settings(projectRef) })
ui.setNotification({ category: 'success', message: 'Successfully updated JWT secret' })
break
case Failed:
@@ -57,9 +55,8 @@ const ServiceList: FC<Props> = ({ projectRef }) => {
}, [jwtSecretUpdateStatus])
// Get the API service
const API_SERVICE_ID = 1
const apiService = services ? services.find((x: any) => x.app.id == API_SERVICE_ID) : {}
const apiConfig = apiService?.app_config
const apiService = settings?.autoApiService
const apiUrl = `${apiService?.protocol ?? 'https'}://${apiService?.endpoint ?? '-'}`
return (
<div>
@@ -79,9 +76,7 @@ const ServiceList: FC<Props> = ({ projectRef }) => {
readOnly
disabled
className="input-mono"
value={`${IS_PLATFORM ? 'https' : PROJECT_ENDPOINT_PROTOCOL}://${
apiConfig?.endpoint ?? '-'
}`}
value={apiUrl}
descriptionText="A RESTful endpoint for querying and managing your database."
layout="horizontal"
/>
@@ -134,7 +134,7 @@ const CustomDomainVerify = ({ projectRef, customDomain, settings }: CustomDomain
<DNSRecord
type="CNAME"
name={customDomain.hostname}
value={settings?.autoApiService.app_config.endpoint ?? 'Loading...'}
value={settings?.autoApiService.endpoint ?? 'Loading...'}
/>
)}
@@ -26,7 +26,7 @@ const CustomDomainsConfigureHostname = () => {
const { data: settings } = useProjectSettingsQuery({ projectRef: ref })
const FORM_ID = 'custom-domains-form'
const endpoint = settings?.autoApiService.app_config.endpoint ?? undefined
const endpoint = settings?.autoApiService.endpoint
const canConfigureCustomDomain = checkPermissions(PermissionAction.UPDATE, 'projects')
const verifyCNAME = async (domain: string): Promise<boolean> => {
@@ -123,7 +123,7 @@ const CustomDomainsConfigureHostname = () => {
'your custom domain'
)}
, resolving to{' '}
{endpoint !== undefined ? (
{endpoint ? (
<code className="text-xs">{endpoint}</code>
) : (
"your project's API URL"
@@ -1,6 +1,6 @@
import { FC } from 'react'
import { observer } from 'mobx-react-lite'
import { PostgresTable } from '@supabase/postgres-meta'
import type { PostgresTable } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { checkPermissions, useStore } from 'hooks'
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
@@ -1,7 +1,7 @@
import { FC } from 'react'
import Link from 'next/link'
import { Button, IconAlertCircle, IconLock } from 'ui'
import { PostgresPolicy, PostgresTable } from '@supabase/postgres-meta'
import type { PostgresPolicy, PostgresTable } from '@supabase/postgres-meta'
import { useStore } from 'hooks'
@@ -1,7 +1,7 @@
import React, { FC } from 'react'
import { isNil } from 'lodash'
import { Select } from 'ui'
import { PostgresType } from '@supabase/postgres-meta'
import type { PostgresType } from '@supabase/postgres-meta'
import InputWithSuggestions from './InputWithSuggestions'
import { POSTGRES_DATA_TYPES } from '../SidePanelEditor.constants'
@@ -3,7 +3,7 @@ import { FC, useEffect, useState } from 'react'
import { isUndefined, isEmpty } from 'lodash'
import { Dictionary } from 'components/grid'
import { Checkbox, SidePanel, Input, Button, IconExternalLink, Toggle } from 'ui'
import {
import type {
PostgresColumn,
PostgresExtension,
PostgresRelationship,
@@ -1,6 +1,6 @@
import { find, isUndefined, isEqual, isNull } from 'lodash'
import { Dictionary } from 'components/grid'
import {
import type {
PostgresColumn,
PostgresRelationship,
PostgresTable,
@@ -5,7 +5,7 @@ import { Button, IconArrowRight } from 'ui'
import { ColumnField } from '../SidePanelEditor.types'
import InformationBox from 'components/ui/InformationBox'
import { getForeignKeyUIState } from './ColumnEditor.utils'
import { PostgresRelationship } from '@supabase/postgres-meta'
import type { PostgresRelationship } from '@supabase/postgres-meta'
interface Props {
column: ColumnField
@@ -1,6 +1,6 @@
import React, { FC, ReactNode } from 'react'
import { IconCalendar, IconType, IconHash, Listbox, IconToggleRight, Input } from 'ui'
import { PostgresType } from '@supabase/postgres-meta'
import type { PostgresType } from '@supabase/postgres-meta'
import { POSTGRES_DATA_TYPES, POSTGRES_DATA_TYPE_OPTIONS } from '../SidePanelEditor.constants'
import { PostgresDataTypeOption } from '../SidePanelEditor.types'
@@ -1,4 +1,4 @@
import { PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
import type { PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
interface Props {
table: PostgresTable
@@ -2,7 +2,7 @@ import React, { FC, useEffect, useState } from 'react'
import { get, find, isEmpty, sortBy } from 'lodash'
import { Dictionary } from 'components/grid'
import { SidePanel, Input, Listbox, IconHelpCircle } from 'ui'
import { PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
import type { PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
import ActionBar from '../ActionBar'
import { ForeignKey } from './ForeignKeySelector.types'
@@ -71,7 +71,7 @@ const ForeignKeySelector: FC<Props> = ({
setSelectedForeignKey({
schema: table.schema,
table: table.name,
column: table.columns.length > 0 ? table.columns[0].name : undefined,
column: table.columns?.length ? table.columns[0].name : undefined,
})
}
}
@@ -1,6 +1,6 @@
import React, { FC, useEffect, useState } from 'react'
import { SidePanel, IconLoader, IconXCircle } from 'ui'
import { PostgresRelationship } from '@supabase/postgres-meta'
import type { PostgresRelationship } from '@supabase/postgres-meta'
import ActionBar from '../ActionBar'
import InputField from './InputField'
@@ -3,7 +3,7 @@ import { isUndefined, partition, isEmpty } from 'lodash'
import { SidePanel } from 'ui'
import { Dictionary } from 'components/grid'
import { Query } from 'components/grid/query/Query'
import { PostgresTable } from '@supabase/postgres-meta'
import type { PostgresTable } from '@supabase/postgres-meta'
import { useStore } from 'hooks'
import ActionBar from '../ActionBar'
@@ -1,4 +1,4 @@
import { PostgresRelationship } from '@supabase/postgres-meta'
import type { PostgresRelationship } from '@supabase/postgres-meta'
export interface JsonEditValue {
column: string
@@ -1,7 +1,7 @@
import dayjs from 'dayjs'
import { find, isUndefined, compact, isEqual, omitBy, isNull, isString } from 'lodash'
import { Dictionary } from 'components/grid'
import { PostgresTable } from '@supabase/postgres-meta'
import type { PostgresTable } from '@supabase/postgres-meta'
import { uuidv4, minifyJSON, tryParseJson } from 'lib/helpers'
import { RowField } from './RowEditor.types'
@@ -15,7 +15,7 @@ export const generateRowFields = (
// @ts-ignore
const primaryKeyColumns = primary_keys.map((key) => key.name)
return table.columns.map((column) => {
return table.columns!.map((column) => {
const value = isUndefined(row)
? ''
: DATETIME_TYPES.includes(column.format)
@@ -2,7 +2,7 @@ import { FC, useState } from 'react'
import { find, isEmpty, isUndefined } from 'lodash'
import { Query, Dictionary } from 'components/grid'
import { Modal } from 'ui'
import { PostgresRelationship, PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
import type { PostgresRelationship, PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
import { useStore } from 'hooks'
import { RowEditor, ColumnEditor, TableEditor } from '.'
@@ -1,4 +1,4 @@
import { PostgresRelationship } from '@supabase/postgres-meta'
import type { PostgresRelationship } from '@supabase/postgres-meta'
export interface CreateColumnPayload {
tableId: number
@@ -1,7 +1,7 @@
import { FC } from 'react'
import { isUndefined } from 'lodash'
import { Checkbox, Input, IconX, IconMenu, Popover, IconLink, IconSettings, Button } from 'ui'
import { PostgresType } from '@supabase/postgres-meta'
import type { PostgresType } from '@supabase/postgres-meta'
import { ColumnField } from '../SidePanelEditor.types'
import ColumnType from '../ColumnEditor/ColumnType'
@@ -3,7 +3,7 @@ import React, { FC, useState } from 'react'
import { partition, isEmpty, isUndefined } from 'lodash'
import { Alert, Button, IconEdit, IconHelpCircle, IconKey, IconTrash, IconExternalLink } from 'ui'
import {
import type {
PostgresTable,
PostgresColumn,
PostgresRelationship,
@@ -1,5 +1,5 @@
import { FC } from 'react'
import { PostgresTable } from '@supabase/postgres-meta'
import type { PostgresTable } from '@supabase/postgres-meta'
interface Props {
schema: string
@@ -1,7 +1,7 @@
import { FC, useEffect, useState } from 'react'
import { isUndefined, isEmpty } from 'lodash'
import { Badge, Checkbox, SidePanel, Input, Alert } from 'ui'
import { PostgresTable, PostgresType } from '@supabase/postgres-meta'
import type { PostgresTable, PostgresType } from '@supabase/postgres-meta'
import { useStore } from 'hooks'
import ActionBar from '../ActionBar'
@@ -1,5 +1,5 @@
import { some } from 'lodash'
import { PostgresColumn, PostgresTable } from '@supabase/postgres-meta'
import type { PostgresColumn, PostgresTable } from '@supabase/postgres-meta'
import { ImportContent, TableField } from './TableEditor.types'
import { DEFAULT_COLUMNS } from './TableEditor.constants'
@@ -2,7 +2,7 @@ import { FC, useRef, useEffect, useState } from 'react'
import { observer } from 'mobx-react-lite'
import { useRouter } from 'next/router'
import { find, isUndefined } from 'lodash'
import { PostgresColumn, PostgresTable } from '@supabase/postgres-meta'
import type { PostgresColumn, PostgresTable } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { SchemaView } from 'types'
@@ -92,7 +92,7 @@ const TableGridEditor: FC<Props> = ({
? parseSupaTable(
{
table: selectedTable as PostgresTable,
columns: (selectedTable as PostgresTable).columns,
columns: (selectedTable as PostgresTable).columns ?? [],
primaryKeys: (selectedTable as PostgresTable).primary_keys,
relationships: (selectedTable as PostgresTable).relationships,
},
@@ -1,6 +1,7 @@
import { Project } from 'types'
import { ProductMenuGroup } from 'components/ui/ProductMenu/ProductMenu.types'
import { useFlag } from 'hooks'
import { IS_PLATFORM } from 'lib/constants'
export const generateDatabaseMenu = (project?: Project): ProductMenuGroup[] => {
const ref = project?.ref ?? 'default'
@@ -62,12 +63,16 @@ export const generateDatabaseMenu = (project?: Project): ProductMenuGroup[] => {
},
]
: []),
{
name: 'Backups',
key: 'backups',
url: `/project/${ref}/database/backups/scheduled`,
items: [],
},
...(IS_PLATFORM
? [
{
name: 'Backups',
key: 'backups',
url: `/project/${ref}/database/backups/scheduled`,
items: [],
},
]
: []),
],
},
]
@@ -3,7 +3,8 @@ import { useRouter } from 'next/router'
import { find, filter, get as _get } from 'lodash'
import { observer } from 'mobx-react-lite'
import { useProjectSettings, useStore, withAuth } from 'hooks'
import { AutoApiService, useProjectSettingsQuery } from 'data/config/project-settings-query'
import { useStore, withAuth } from 'hooks'
import BaseLayout from 'components/layouts'
import ProjectLayout from '../ProjectLayout/ProjectLayout'
import StorageMenu from './StorageMenu'
@@ -39,20 +40,24 @@ const StorageLayout: FC<Props> = ({ title, children }) => {
buckets,
} = storageExplorerStore || {}
const { services, isLoading } = useProjectSettings(ref as string | undefined)
const apiService = find(services ?? [], (service) => service.app.id === 1)
const projectUrl = apiService?.app_config?.endpoint ?? ''
const { data: settings, isLoading } = useProjectSettingsQuery({ projectRef: ref as string })
const apiService = settings?.autoApiService
const serviceKey = find(apiService?.service_api_keys ?? [], (key) => key.tags === 'service_role')
const canAccessStorage = !isLoading && services && serviceKey
const canAccessStorage = !isLoading && apiService && serviceKey
useEffect(() => {
if (!isLoading && services) initializeStorageStore()
if (!isLoading && apiService) initializeStorageStore(apiService)
}, [isLoading])
const initializeStorageStore = async () => {
if (projectUrl) {
const initializeStorageStore = async (apiService: AutoApiService) => {
if (apiService.endpoint) {
if (serviceKey) {
storageExplorerStore.initStore(ref, projectUrl, serviceKey.api_key)
storageExplorerStore.initStore(
ref,
apiService.endpoint,
apiService.serviceApiKey,
apiService.protocol
)
await storageExplorerStore.fetchBuckets()
}
} else {
@@ -1,7 +1,7 @@
import { FC, ReactNode, useState, useEffect } from 'react'
import { isUndefined } from 'lodash'
import { observer } from 'mobx-react-lite'
import { PostgresTable } from '@supabase/postgres-meta'
import type { PostgresTable } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { checkPermissions, useParams, useStore } from 'hooks'
@@ -20,7 +20,7 @@ import {
} from 'ui'
import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { PostgresSchema, PostgresTable } from '@supabase/postgres-meta'
import type { PostgresSchema, PostgresTable } from '@supabase/postgres-meta'
import { SchemaView } from 'types'
import { checkPermissions, useStore, useParams } from 'hooks'
@@ -26,7 +26,7 @@ const FileExplorer = ({
const fileExplorerRef = useRef(null)
const storageExplorerStore = useStorageStore()
const { setSelectedItemToRename } = storageExplorerStore
const { setSelectedItemToRename, setSelectedItemsToDelete } = storageExplorerStore
useEffect(() => {
if (fileExplorerRef) {
@@ -3,8 +3,8 @@ import { IconAlertCircle, IconLoader, Input } from 'ui'
import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { checkPermissions, useJwtSecretUpdateStatus, useProjectSettings } from 'hooks'
import { DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants'
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
import { checkPermissions, useJwtSecretUpdateStatus } from 'hooks'
import Panel from 'components/ui/Panel'
const DisplayApiSettings = () => {
@@ -12,11 +12,10 @@ const DisplayApiSettings = () => {
const { ref } = router.query
const {
services,
error: projectSettingsError,
data: settings,
isError: isProjectSettingsError,
isLoading: isProjectSettingsLoading,
} = useProjectSettings(ref as string | undefined)
} = useProjectSettingsQuery({ projectRef: ref as string })
const {
jwtSecretUpdateStatus,
isError: isJwtSecretUpdateStatusError,
@@ -28,8 +27,7 @@ const DisplayApiSettings = () => {
const isNotUpdatingJwtSecret =
jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated
// Get the API service
const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_PROJECT_API_SERVICE_ID)
const apiKeys = apiService?.service_api_keys ?? []
const apiKeys = settings?.autoApiService.service_api_keys ?? []
// api keys should not be empty. However it can be populated with a delay on project creation
const isApiKeysEmpty = apiKeys.length === 0
@@ -3,8 +3,7 @@ import { useRouter } from 'next/router'
import { Input, IconAlertCircle, IconLoader } from 'ui'
import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events'
import { useJwtSecretUpdateStatus, useProjectSettings } from 'hooks'
import { DEFAULT_PROJECT_API_SERVICE_ID, IS_PLATFORM } from 'lib/constants'
import { PROJECT_ENDPOINT_PROTOCOL } from 'pages/api/constants'
import { DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants'
import Panel from 'components/ui/Panel'
const DisplayConfigSettings = () => {
@@ -28,6 +27,7 @@ const DisplayConfigSettings = () => {
const jwtSecret = project?.jwt_secret ?? ''
const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_PROJECT_API_SERVICE_ID)
const apiConfig = apiService?.app_config ?? {}
const apiUrl = `${apiConfig.protocol ?? 'https'}://${apiConfig.endpoint ?? '-'}`
return (
<ConfigContentWrapper>
@@ -56,7 +56,7 @@ const DisplayConfigSettings = () => {
copy
disabled
className="input-mono"
value={`${IS_PLATFORM ? 'https' : PROJECT_ENDPOINT_PROTOCOL}://${apiConfig.endpoint}`}
value={apiUrl}
descriptionText="A RESTful endpoint for querying and managing your database."
layout="horizontal"
/>
@@ -48,6 +48,7 @@ export type ServiceApiKey = {
}
export type AutoApiService = Service & {
protocol: 'https' | 'http'
endpoint: string
restUrl: string
project: Project
-1
View File
@@ -1,5 +1,4 @@
import { v4 as _uuidV4 } from 'uuid'
import { PostgresColumn } from '@supabase/postgres-meta'
import { post } from 'lib/common/fetch'
import { PASSWORD_STRENGTH, DEFAULT_MINIMUM_PASSWORD_STRENGTH, API_URL } from 'lib/constants'
@@ -97,17 +97,16 @@ class StorageExplorerStore {
}
}
initStore(projectRef, url, serviceKey) {
initStore(projectRef, url, serviceKey, protocol = PROJECT_ENDPOINT_PROTOCOL) {
this.projectRef = projectRef
this.initializeSupabaseClient(serviceKey, url)
this.initializeSupabaseClient(serviceKey, url, protocol)
}
/* Methods which are commonly used + For better readability */
initializeSupabaseClient = (serviceKey, serviceEndpoint) => {
console.debug(serviceEndpoint)
initializeSupabaseClient = (serviceKey, serviceEndpoint, protocol) => {
this.supabaseClient = createClient(
`${IS_PLATFORM ? 'https' : PROJECT_ENDPOINT_PROTOCOL}://${serviceEndpoint}`,
`${IS_PLATFORM ? 'https' : protocol}://${serviceEndpoint}`,
serviceKey,
{
auth: {
+13
View File
@@ -131,6 +131,19 @@ const nextConfig = {
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-Content-Type-Options',
value: 'no-sniff',
},
{
key: 'Content-Security-Policy',
value: `
frame-ancestors 'none';
upgrade-insecure-requests;
`
.replace(/\s{2,}/g, ' ')
.trim(),
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
+1 -1
View File
@@ -26,7 +26,6 @@
"@sentry/nextjs": "^6.17.4",
"@stripe/react-stripe-js": "^1.7.0",
"@stripe/stripe-js": "^1.25.0",
"@supabase/postgres-meta": "^0.26.1",
"@supabase/react-data-grid": "^7.1.0-beta.7",
"@supabase/shared-types": "^0.1.29",
"@supabase/supabase-js": "^2.1.1",
@@ -102,6 +101,7 @@
"@storybook/addon-links": "^6.4.19",
"@storybook/react": "^6.4.19",
"@storybook/testing-library": "^0.0.9",
"@supabase/postgres-meta": "^0.60.2",
"@tailwindcss/typography": "^0.5.2",
"@testing-library/dom": "^8.19.0",
"@testing-library/react": "^12.1.5",
+1 -1
View File
@@ -1,4 +1,4 @@
const PUBLIC_URL = new URL(process.env.NEXT_PUBLIC_SUPABASE_PUBLIC_URL || 'http://localhost:8000')
const PUBLIC_URL = new URL(process.env.SUPABASE_PUBLIC_URL || 'http://localhost:8000')
export const PROJECT_REST_URL = `${PUBLIC_URL.origin}/rest/v1/`
export const PROJECT_ENDPOINT = PUBLIC_URL.host
@@ -1,20 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import apiWrapper from 'lib/api/apiWrapper'
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
async function handler(req: NextApiRequest, res: NextApiResponse) {
const { method } = req
switch (method) {
case 'GET':
return handleGetAll(req, res)
default:
res.setHeader('Allow', ['GET'])
res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
}
}
const handleGetAll = async (req: NextApiRequest, res: NextApiResponse) => {
return res.status(200).json({ tierKey: 'FREE', backups: [] })
}
+2 -6
View File
@@ -1,7 +1,7 @@
import { NextApiRequest, NextApiResponse } from 'next'
import apiWrapper from 'lib/api/apiWrapper'
import { PROJECT_ENDPOINT, PROJECT_REST_URL } from 'pages/api/constants'
import { PROJECT_ENDPOINT, PROJECT_ENDPOINT_PROTOCOL, PROJECT_REST_URL } from 'pages/api/constants'
export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)
@@ -36,17 +36,12 @@ const handleGetAll = async (req: NextApiRequest, res: NextApiResponse) => {
{
id: 1,
name: 'Default API',
project: { ref: 'default' },
app: { id: 1, name: 'Auto API' },
app_config: {
db_schema: 'public',
endpoint: PROJECT_ENDPOINT,
realtime_enabled: true,
},
endpoint: PROJECT_ENDPOINT,
restUrl: PROJECT_REST_URL,
defaultApiKey: process.env.SUPABASE_ANON_KEY,
serviceApiKey: process.env.SUPABASE_SERVICE_KEY,
service_api_keys: [
{
api_key_encrypted: '-',
@@ -72,6 +67,7 @@ const handleGetAll = async (req: NextApiRequest, res: NextApiResponse) => {
endpoint: PROJECT_ENDPOINT,
realtime_enabled: true,
},
protocol: PROJECT_ENDPOINT_PROTOCOL,
endpoint: PROJECT_ENDPOINT,
restUrl: PROJECT_REST_URL,
defaultApiKey: process.env.SUPABASE_ANON_KEY,
+10 -11
View File
@@ -5,14 +5,13 @@ import { FC, createContext, useContext, useEffect, useState } from 'react'
import { observer, useLocalObservable } from 'mobx-react-lite'
import { NextPageWithLayout } from 'types'
import { API_URL, IS_PLATFORM } from 'lib/constants'
import { PROJECT_ENDPOINT_PROTOCOL } from 'pages/api/constants'
import { checkPermissions, useStore } from 'hooks'
import { get } from 'lib/common/fetch'
import { snakeToCamel } from 'lib/helpers'
import { DocsLayout } from 'components/layouts'
import { GeneralContent, ResourceContent, RpcContent } from 'components/interfaces/Docs'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
const PageContext = createContext(null)
@@ -71,9 +70,11 @@ const DocView: FC<any> = observer(({}) => {
const [selectedLang, setSelectedLang] = useState<any>('js')
const [showApiKey, setShowApiKey] = useState<any>(DEFAULT_KEY)
const { data, error }: any = useSWR(`${API_URL}/props/project/${PageState.projectRef}/api`, get)
const API_KEY = data?.autoApiService?.defaultApiKey
const swaggerUrl = data?.autoApiService?.restUrl
const { data, error } = useProjectSettingsQuery({ projectRef: PageState.projectRef as string })
const API_KEY = data?.autoApiService.service_api_keys.find((key: any) => key.tags === 'anon')
? data.autoApiService.defaultApiKey
: undefined
const swaggerUrl = data?.autoApiService.restUrl
const headers: any = { apikey: API_KEY }
const canReadServiceKey = checkPermissions(
@@ -81,7 +82,7 @@ const DocView: FC<any> = observer(({}) => {
'service_api_keys.service_role_key'
)
if (API_KEY?.length > 40) headers['Authorization'] = `Bearer ${API_KEY}`
if (API_KEY) headers['Authorization'] = `Bearer ${API_KEY}`
const { data: jsonSchema, error: jsonSchemaError } = useSWR(
() => swaggerUrl,
@@ -117,9 +118,7 @@ const DocView: FC<any> = observer(({}) => {
// Data Loaded
const autoApiService = {
...data.autoApiService,
endpoint: IS_PLATFORM
? `https://${data?.autoApiService?.endpoint}`
: `${PROJECT_ENDPOINT_PROTOCOL}://${data.autoApiService.endpoint}`,
endpoint: `${data.autoApiService.protocol ?? 'https'}://${data.autoApiService.endpoint ?? '-'}`,
}
const { query } = router
@@ -175,7 +174,7 @@ const DocView: FC<any> = observer(({}) => {
key="anon"
onClick={() =>
setShowApiKey({
key: autoApiService?.defaultApiKey,
key: API_KEY,
name: 'anon (public)',
})
}
@@ -187,7 +186,7 @@ const DocView: FC<any> = observer(({}) => {
key="service"
onClick={() =>
setShowApiKey({
key: autoApiService?.serviceApiKey,
key: autoApiService.serviceApiKey,
name: 'service_role (secret)',
})
}
@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import { observer } from 'mobx-react-lite'
import { isUndefined } from 'lodash'
import { PostgresColumn, PostgresTable } from '@supabase/postgres-meta'
import type { PostgresColumn, PostgresTable } from '@supabase/postgres-meta'
import { Modal } from 'ui'
import { useStore } from 'hooks'
+1 -1
View File
@@ -5,7 +5,7 @@ import { useRouter } from 'next/router'
import { observer } from 'mobx-react-lite'
import { isUndefined, isNaN } from 'lodash'
import { Alert, Button, Checkbox, IconExternalLink, Modal } from 'ui'
import { PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
import type { PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
import { useStore, withAuth, useUrlState, useParams } from 'hooks'
import { Dictionary } from 'components/grid'
+1 -1
View File
@@ -3,7 +3,7 @@ import { NextPage } from 'next'
import router from 'next/router'
import { observer } from 'mobx-react-lite'
import { isUndefined } from 'lodash'
import { PostgresTable } from '@supabase/postgres-meta'
import type { PostgresTable } from '@supabase/postgres-meta'
import { useStore, withAuth } from 'hooks'
import { TableEditorLayout } from 'components/layouts'
@@ -6,7 +6,8 @@ import { IconGlobe, IconTerminal } from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { NextPageWithLayout } from 'types'
import { checkPermissions, useProjectSettings, useStore } from 'hooks'
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
import { checkPermissions, useStore } from 'hooks'
import FunctionsLayout from 'components/layouts/FunctionsLayout'
import CommandRender from 'components/interfaces/Functions/CommandRender'
import NoPermission from 'components/ui/NoPermission'
@@ -95,16 +96,14 @@ const PageLayout: NextPageWithLayout = () => {
},
]
const { services } = useProjectSettings(ref as string | undefined)
const API_SERVICE_ID = 1
// Get the API service
const apiService = (services ?? []).find((x: any) => x.app.id == API_SERVICE_ID)
const apiKeys = apiService?.service_api_keys ?? []
const anonKey = apiKeys.find((x: any) => x.name === 'anon key')?.api_key
const { data: settings } = useProjectSettingsQuery({ projectRef: ref as string })
const apiService = settings?.autoApiService
const anonKey = apiService?.service_api_keys.find((x: any) => x.name === 'anon key')
? apiService.defaultApiKey
: undefined
const endpoint = apiService?.app_config.endpoint ?? ''
const endpoint = apiService?.endpoint ?? ''
const endpointSections = endpoint.split('.')
const functionsEndpoint = [
...endpointSections.slice(0, 1),
+2 -2
View File
@@ -370,8 +370,8 @@ const ProjectLinks: FC = observer(() => {
// Then create env for vercel project with supabase project
const vercelEnvs = prepareVercelEvns(defaultVercelEnvs, {
endpoint: `${IS_PLATFORM ? 'https' : PROJECT_ENDPOINT_PROTOCOL}://${
projectDetails.autoApiService.endpoint
endpoint: `${projectDetails.apiService.protocol ?? 'https'}://${
projectDetails.apiService.endpoint ?? '-'
}`,
anon_key: projectDetails.autoApiService.defaultApiKey,
service_key: projectDetails.autoApiService.serviceApiKey,
+1 -1
View File
@@ -1,5 +1,5 @@
import PostgresMetaInterface from '../common/PostgresMetaInterface'
import { PostgresColumn } from '@supabase/postgres-meta'
import type { PostgresColumn } from '@supabase/postgres-meta'
import { IRootStore } from '../RootStore'
export default class ColumnStore extends PostgresMetaInterface<PostgresColumn> {
+1 -1
View File
@@ -1,5 +1,5 @@
import PostgresMetaInterface from '../common/PostgresMetaInterface'
import { PostgresColumn } from '@supabase/postgres-meta'
import type { PostgresColumn } from '@supabase/postgres-meta'
import { IRootStore } from '../RootStore'
export default class ExtensionsStore extends PostgresMetaInterface<PostgresColumn> {
+1 -1
View File
@@ -1,4 +1,4 @@
import { PostgresTable } from '@supabase/postgres-meta'
import type { PostgresTable } from '@supabase/postgres-meta'
import PostgresMetaInterface from '../common/PostgresMetaInterface'
import { IRootStore } from '../RootStore'
+1 -1
View File
@@ -3,7 +3,7 @@ import { makeObservable, observable } from 'mobx'
import { find, isUndefined, isEqual, isEmpty, chunk } from 'lodash'
import { Query } from 'components/grid/query/Query'
import {
import type {
PostgresColumn,
PostgresTable,
PostgresRelationship,
+1 -1
View File
@@ -1,4 +1,4 @@
import { PostgresSchema } from '@supabase/postgres-meta'
import type { PostgresSchema } from '@supabase/postgres-meta'
import PostgresMetaInterface from '../common/PostgresMetaInterface'
import { IRootStore } from '../RootStore'
+1 -1
View File
@@ -1,5 +1,5 @@
import { action, makeObservable } from 'mobx'
import { PostgresTable } from '@supabase/postgres-meta'
import type { PostgresTable } from '@supabase/postgres-meta'
import PostgresMetaInterface, { IPostgresMetaInterface } from '../common/PostgresMetaInterface'
import { IRootStore } from '../RootStore'
+1 -1
View File
@@ -1,5 +1,5 @@
import PostgresMetaInterface from '../common/PostgresMetaInterface'
import { PostgresType } from '@supabase/postgres-meta'
import type { PostgresType } from '@supabase/postgres-meta'
import { IRootStore } from '../RootStore'
export default class TypesStore extends PostgresMetaInterface<PostgresType> {
+1 -1
View File
@@ -1,4 +1,4 @@
import { PostgresColumn } from '@supabase/postgres-meta'
import type { PostgresColumn } from '@supabase/postgres-meta'
import { Query } from 'components/grid'
import { makeAutoObservable } from 'mobx'
import { SchemaView, VaultSecret } from 'types'
+1 -1
View File
@@ -1,4 +1,4 @@
import { PostgresColumn } from '@supabase/postgres-meta'
import type { PostgresColumn } from '@supabase/postgres-meta'
export interface Notification {
category: 'info' | 'error' | 'success' | 'loading'