From 0271ab45caeac2be3999f8947f5fb53754ed714e Mon Sep 17 00:00:00 2001 From: Benjamin Dobell Date: Sun, 2 Oct 2022 03:41:01 +1100 Subject: [PATCH 01/47] Redirect URLs, not Redirect Domains --- .../interfaces/Auth/Auth.constants.ts | 6 +- .../RedirectUrlList.tsx} | 14 +-- .../RedirectUrls.tsx} | 95 +++++++++++-------- .../ValueContainer.tsx | 0 .../interfaces/Auth/SmtpForm/SmtpForm.tsx | 4 +- studio/pages/project/[ref]/auth/settings.tsx | 4 +- .../AuthProvidersFormValidation.tsx | 12 +-- .../components/Auth/Auth.constants.test.js | 40 ++++---- 8 files changed, 93 insertions(+), 82 deletions(-) rename studio/components/interfaces/Auth/{RedirectDomains/DomainList.tsx => RedirectUrls/RedirectUrlList.tsx} (82%) rename studio/components/interfaces/Auth/{RedirectDomains/RedirectDomains.tsx => RedirectUrls/RedirectUrls.tsx} (64%) rename studio/components/interfaces/Auth/{RedirectDomains => RedirectUrls}/ValueContainer.tsx (100%) diff --git a/studio/components/interfaces/Auth/Auth.constants.ts b/studio/components/interfaces/Auth/Auth.constants.ts index 32f80bc5cb2..aac17a564ec 100644 --- a/studio/components/interfaces/Auth/Auth.constants.ts +++ b/studio/components/interfaces/Auth/Auth.constants.ts @@ -7,7 +7,7 @@ // "https://uptime-monitor-fe.vercel.app/" // Supports wildcards, port numbers at the end, paths at the end and query params -const baseDomainRegex = +const baseUrlRegex = /^((ftp|http|https):\/\/)?(www.)?(?!.*(ftp|http|https|www.))[a-zA-Z0-9_*-]+(\.[a-zA-Z0-9_*-]+)+((\/)[\w#]+)*(\/\w+\?[a-zA-Z0-9_]+=\w+(&[a-zA-Z0-9_]+=\w+)*)+(?:\.[a-z]+)*(?::\d+)?(?![^<]*(?:<\/\w+>|\/?>))(.*)?\/?(.)*?$/gm // iOS deep linking scheme https://benoitpasquier.com/deep-linking-url-scheme-ios/ @@ -21,7 +21,7 @@ const localhostRegex = /^(?:^|\s)((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+ const chromeExtensionRegex = /chrome-extension:\/\/([a-zA-Z]*)/gm // combine the above regexes -export const domainRegex = new RegExp( - `(${baseDomainRegex.source})|(${localhostRegex.source})|(${appRegex.source})|(${chromeExtensionRegex.source})`, +export const urlRegex = new RegExp( + `(${baseUrlRegex.source})|(${localhostRegex.source})|(${appRegex.source})|(${chromeExtensionRegex.source})`, 'i' ) diff --git a/studio/components/interfaces/Auth/RedirectDomains/DomainList.tsx b/studio/components/interfaces/Auth/RedirectUrls/RedirectUrlList.tsx similarity index 82% rename from studio/components/interfaces/Auth/RedirectDomains/DomainList.tsx rename to studio/components/interfaces/Auth/RedirectUrls/RedirectUrlList.tsx index 08929879c79..72eeab1194e 100644 --- a/studio/components/interfaces/Auth/RedirectDomains/DomainList.tsx +++ b/studio/components/interfaces/Auth/RedirectUrls/RedirectUrlList.tsx @@ -9,10 +9,10 @@ import { HorizontalShimmerWithIcon } from 'components/ui/Shimmers' interface Props { canUpdate: boolean - onSelectDomainToDelete: (domain: string) => void + onSelectUrlToDelete: (url: string) => void } -const DomainList: FC = ({ canUpdate, onSelectDomainToDelete }) => { +const RedirectUrlList: FC = ({ canUpdate, onSelectUrlToDelete }) => { const { authConfig } = useStore() const URI_ALLOW_LIST_ARRAY = authConfig.config.URI_ALLOW_LIST @@ -31,20 +31,20 @@ const DomainList: FC = ({ canUpdate, onSelectDomainToDelete }) => { ) : URI_ALLOW_LIST_ARRAY.length > 0 ? ( - URI_ALLOW_LIST_ARRAY.map((domain: string) => { + URI_ALLOW_LIST_ARRAY.map((url: string) => { return ( - +
- {domain} + {url}
{canUpdate && ( @@ -69,4 +69,4 @@ const DomainList: FC = ({ canUpdate, onSelectDomainToDelete }) => { ) } -export default observer(DomainList) +export default observer(RedirectUrlList) diff --git a/studio/components/interfaces/Auth/RedirectDomains/RedirectDomains.tsx b/studio/components/interfaces/Auth/RedirectUrls/RedirectUrls.tsx similarity index 64% rename from studio/components/interfaces/Auth/RedirectDomains/RedirectDomains.tsx rename to studio/components/interfaces/Auth/RedirectUrls/RedirectUrls.tsx index 5eede56fce7..ad8e9a80e8f 100644 --- a/studio/components/interfaces/Auth/RedirectDomains/RedirectDomains.tsx +++ b/studio/components/interfaces/Auth/RedirectUrls/RedirectUrls.tsx @@ -7,10 +7,10 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { checkPermissions, useStore } from 'hooks' import { FormHeader } from 'components/ui/Forms' -import { domainRegex } from '../Auth.constants' -import DomainList from './DomainList' +import { urlRegex } from '../Auth.constants' +import RedirectUrlList from './RedirectUrlList' -const RedirectDomains = () => { +const RedirectUrls = () => { const { authConfig, ui } = useStore() const URI_ALLOW_LIST_ARRAY = authConfig.config.URI_ALLOW_LIST @@ -19,24 +19,24 @@ const RedirectDomains = () => { const [open, setOpen] = useState(false) const [isDeleting, setIsDeleting] = useState(false) - const [selectedDomainToDelete, setSelectedDomainToDelete] = useState() + const [selectedUrlToDelete, setSelectedUrlToDelete] = useState() const canUpdateConfig = checkPermissions(PermissionAction.UPDATE, 'custom_config_gotrue') - const newDomainSchema = object({ - domain: string().matches(domainRegex, 'URL is not valid').required(), + const newUrlSchema = object({ + url: string().matches(urlRegex, 'URL is not valid').required(), }) - const onAddNewDomain = async (values: any, { setSubmitting }: any) => { + const onAddNewUrl = async (values: any, { setSubmitting }: any) => { setSubmitting(true) const payload = URI_ALLOW_LIST_ARRAY - payload.push(values.domain) + payload.push(values.url) const payloadString = payload.toString() if (payloadString.length > 2 * 1024) { ui.setNotification({ - message: 'Too many redirect domains, please remove some or try to use wildcards', + message: 'Too many redirect URLs, please remove some or try to use wildcards', category: 'error', }) @@ -47,36 +47,36 @@ const RedirectDomains = () => { const { error } = await authConfig.update({ URI_ALLOW_LIST: payloadString }) if (!error) { setOpen(false) - ui.setNotification({ category: 'success', message: 'Successfully added domain' }) + ui.setNotification({ category: 'success', message: 'Successfully added URL' }) } else { ui.setNotification({ error, category: 'error', - message: `Failed to update domain: ${error?.message}`, + message: `Failed to update URL: ${error?.message}`, }) } setSubmitting(false) } - const onConfirmDeleteDomain = async (domain?: string) => { - if (!domain) return + const onConfirmDeleteUrl = async (url?: string) => { + if (!url) return setIsDeleting(true) - // Remove selectedDomain from array and update - const payload = URI_ALLOW_LIST_ARRAY.filter((e: string) => e !== domain) + // Remove selectedUrl from array and update + const payload = URI_ALLOW_LIST_ARRAY.filter((e: string) => e !== url) const { error } = await authConfig.update({ URI_ALLOW_LIST: payload.toString() }) if (!error) { - setSelectedDomainToDelete(undefined) - ui.setNotification({ category: 'success', message: 'Successfully removed domain' }) + setSelectedUrlToDelete(undefined) + ui.setNotification({ category: 'success', message: 'Successfully removed URL' }) } else { ui.setNotification({ error, category: 'error', - message: `Failed to remove domain: ${error?.message}`, + message: `Failed to remove URL: ${error?.message}`, }) } @@ -88,12 +88,12 @@ const RedirectDomains = () => {
{!canUpdateConfig && ( @@ -113,36 +113,47 @@ const RedirectDomains = () => { )}
- + setOpen(!open)} - header={

Add a new domain

} + header={

Add a new URL

} >
{({ isSubmitting }: { isSubmitting: boolean }) => { return (

- This will add a domain to a list of allowed domains that can interact with your - Authenticaton services for this project. + This will add a URL to a list of allowed URLs that can interact with your + Authentication services for this project. +

+

+ Supports{' '} + + globs + + {' '}where . and / are used as delimiters.

@@ -150,13 +161,13 @@ const RedirectDomains = () => {
@@ -167,18 +178,18 @@ const RedirectDomains = () => { Remove domain} - onCancel={() => setSelectedDomainToDelete(undefined)} + visible={selectedUrlToDelete !== undefined} + header={

Remove URL

} + onCancel={() => setSelectedUrlToDelete(undefined)} >

Are you sure you want to remove{' '} - {selectedDomainToDelete}? + {selectedUrlToDelete}?

- This domain will no longer work with your authentication configuration. + This URL will no longer work with your authentication configuration.

@@ -187,7 +198,7 @@ const RedirectDomains = () => { block type="default" size="medium" - onClick={() => setSelectedDomainToDelete(undefined)} + onClick={() => setSelectedUrlToDelete(undefined)} > Cancel @@ -196,9 +207,9 @@ const RedirectDomains = () => { size="medium" type="warning" loading={isDeleting} - onClick={() => onConfirmDeleteDomain(selectedDomainToDelete)} + onClick={() => onConfirmDeleteUrl(selectedUrlToDelete)} > - {isDeleting ? 'Removing...' : 'Remove domain'} + {isDeleting ? 'Removing...' : 'Remove URL'}
@@ -207,4 +218,4 @@ const RedirectDomains = () => { ) } -export default observer(RedirectDomains) +export default observer(RedirectUrls) diff --git a/studio/components/interfaces/Auth/RedirectDomains/ValueContainer.tsx b/studio/components/interfaces/Auth/RedirectUrls/ValueContainer.tsx similarity index 100% rename from studio/components/interfaces/Auth/RedirectDomains/ValueContainer.tsx rename to studio/components/interfaces/Auth/RedirectUrls/ValueContainer.tsx diff --git a/studio/components/interfaces/Auth/SmtpForm/SmtpForm.tsx b/studio/components/interfaces/Auth/SmtpForm/SmtpForm.tsx index 888e2c4d161..f6eb10046a4 100644 --- a/studio/components/interfaces/Auth/SmtpForm/SmtpForm.tsx +++ b/studio/components/interfaces/Auth/SmtpForm/SmtpForm.tsx @@ -13,7 +13,7 @@ import { FormSectionLabel, } from 'components/ui/Forms' import { useStore, checkPermissions } from 'hooks' -import { domainRegex } from './../Auth.constants' +import { urlRegex } from './../Auth.constants' import { defaultDisabledSmtpFormValues } from './SmtpForm.constants' import { generateFormValues, isSmtpEnabled } from './SmtpForm.utils' @@ -55,7 +55,7 @@ const SmtpForm = () => { }, then: (schema) => schema - .matches(domainRegex, 'Must be a valid URL or IP address') + .matches(urlRegex, 'Must be a valid URL or IP address') .required('Host URL is required.'), otherwise: (schema) => schema, }), diff --git a/studio/pages/project/[ref]/auth/settings.tsx b/studio/pages/project/[ref]/auth/settings.tsx index ea14e36bc0d..0bc8ed2ca78 100644 --- a/studio/pages/project/[ref]/auth/settings.tsx +++ b/studio/pages/project/[ref]/auth/settings.tsx @@ -6,7 +6,7 @@ import { NextPageWithLayout } from 'types' import { checkPermissions, useStore } from 'hooks' import { AuthProvidersForm } from 'components/interfaces' import AutoSchemaForm from 'components/interfaces/Auth/AutoSchemaForm' -import RedirectDomains from 'components/interfaces/Auth/RedirectDomains/RedirectDomains' +import RedirectUrls from 'components/interfaces/Auth/RedirectUrls/RedirectUrls' import { AuthLayout } from 'components/layouts' import { FormsContainer } from 'components/ui/Forms' import NoPermission from 'components/ui/NoPermission' @@ -26,7 +26,7 @@ const PageLayout: NextPageWithLayout = () => { return ( - + ) diff --git a/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx b/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx index 3ceb9e46087..7d10bf80748 100644 --- a/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx +++ b/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx @@ -1,5 +1,5 @@ import { boolean, number, object, string } from 'yup' -import { domainRegex } from 'components/interfaces/Auth/Auth.constants' +import { urlRegex } from 'components/interfaces/Auth/Auth.constants' const JSON_SCHEMA_VERSION = 'http://json-schema.org/draft-07/schema#' @@ -379,7 +379,7 @@ const EXTERNAL_PROVIDER_AZURE = { then: (schema) => schema.required('Secret ID is required'), otherwise: (schema) => schema, }), - EXTERNAL_AZURE_URL: string().matches(domainRegex, 'Must be a valid URL').optional(), + EXTERNAL_AZURE_URL: string().matches(urlRegex, 'Must be a valid URL').optional(), }), misc: { iconKey: 'microsoft-icon', @@ -577,7 +577,7 @@ const EXTERNAL_PROVIDER_GITLAB = { then: (schema) => schema.required('Client Secret is required'), otherwise: (schema) => schema, }), - EXTERNAL_GITLAB_URL: string().matches(domainRegex, 'Must be a valid URL').optional(), + EXTERNAL_GITLAB_URL: string().matches(urlRegex, 'Must be a valid URL').optional(), }), misc: { iconKey: 'gitlab-icon', @@ -665,8 +665,8 @@ const EXTERNAL_PROVIDER_KEYCLOAK = { EXTERNAL_KEYCLOAK_URL: string().when('EXTERNAL_KEYCLOAK_ENABLED', { is: true, then: (schema) => - schema.matches(domainRegex, 'Must be a valid URL').required('Realm URL is required'), - otherwise: (schema) => schema.matches(domainRegex, 'Must be a valid URL'), + schema.matches(urlRegex, 'Must be a valid URL').required('Realm URL is required'), + otherwise: (schema) => schema.matches(urlRegex, 'Must be a valid URL'), }), }), misc: { @@ -931,7 +931,7 @@ const EXTERNAL_PROVIDER_WORKOS = { validationSchema: object().shape({ EXTERNAL_WORKOS_ENABLED: boolean().required(), EXTERNAL_WORKOS_URL: string() - .matches(domainRegex, 'Must be a valid URL') + .matches(urlRegex, 'Must be a valid URL') .when('EXTERNAL_WORKOS_ENABLED', { is: true, then: (schema) => schema.required('WorkOS URL is required'), diff --git a/studio/tests/components/Auth/Auth.constants.test.js b/studio/tests/components/Auth/Auth.constants.test.js index 4c8a3fe62f3..ab7504014dc 100644 --- a/studio/tests/components/Auth/Auth.constants.test.js +++ b/studio/tests/components/Auth/Auth.constants.test.js @@ -1,87 +1,87 @@ -import { domainRegex } from 'components/interfaces/Auth/Auth.constants' +import { urlRegex } from 'components/interfaces/Auth/Auth.constants' describe('Auth.constants: domainRegex', () => { test('should validate typical URLs', () => { const mockInput1 = 'http://domain.com' - const output1 = domainRegex.test(mockInput1) + const output1 = urlRegex.test(mockInput1) expect(output1).toBe(true) const mockInput2 = 'https://supabase.io' - const output2 = domainRegex.test(mockInput2) + const output2 = urlRegex.test(mockInput2) expect(output2).toBe(true) const mockInput3 = 'https://new-domain-vercel.com' - const output3 = domainRegex.test(mockInput3) + const output3 = urlRegex.test(mockInput3) expect(output3).toBe(true) const mockInput4 = 'www.test-domain.com' - const output4 = domainRegex.test(mockInput4) + const output4 = urlRegex.test(mockInput4) expect(output4).toBe(true) }) test('should validate app-based domains', () => { const mockInput1 = 'exp://exp.host/some-app' - const output1 = domainRegex.test(mockInput1) + const output1 = urlRegex.test(mockInput1) expect(output1).toBe(true) const mockInput2 = 'exp://exp.host/some-app?release-channel=default' - const output2 = domainRegex.test(mockInput2) + const output2 = urlRegex.test(mockInput2) expect(output2).toBe(true) }) test('should validate subdomains', () => { const mockInput1 = 'https://app.supabase.com' - const output1 = domainRegex.test(mockInput1) + const output1 = urlRegex.test(mockInput1) expect(output1).toBe(true) }) test('should validate localhost URLs', () => { const mockInput1 = 'http://localhost:3000' - const output1 = domainRegex.test(mockInput1) + const output1 = urlRegex.test(mockInput1) expect(output1).toBe(true) }) test('should validate URLs with query params', () => { const mockInput1 = 'https://supabase.com?name=test' - const output1 = domainRegex.test(mockInput1) + const output1 = urlRegex.test(mockInput1) expect(output1).toBe(true) const mockInput2 = 'https://supabase.com?name=test&description=hello&page=2' - const output2 = domainRegex.test(mockInput2) + const output2 = urlRegex.test(mockInput2) expect(output2).toBe(true) }) test('should validate URLs with wildcards', () => { const mockInput1 = 'https://supabase*.com' - const output1 = domainRegex.test(mockInput1) + const output1 = urlRegex.test(mockInput1) expect(output1).toBe(true) const mockInput2 = 'https://supabase.com/*' - const output2 = domainRegex.test(mockInput2) + const output2 = urlRegex.test(mockInput2) expect(output2).toBe(true) const mockInput3 = 'https://new-*-domain.com/*' - const output3 = domainRegex.test(mockInput3) + const output3 = urlRegex.test(mockInput3) expect(output3).toBe(true) const mockInput4 = 'https://new-*-domain.com/*/*/*' - const output4 = domainRegex.test(mockInput4) + const output4 = urlRegex.test(mockInput4) expect(output4).toBe(true) const mockInput5 = 'https://sub-*-domain.new-*-domain.com/*/*/*' - const output5 = domainRegex.test(mockInput5) + const output5 = urlRegex.test(mockInput5) expect(output5).toBe(true) }) test('should invalidate invalid URLs', () => { const mockInput1 = 'supabase' - const output1 = domainRegex.test(mockInput1) + const output1 = urlRegex.test(mockInput1) expect(output1).toBe(false) const mockInput2 = 'mailto:test@gmail.com' - const output2 = domainRegex.test(mockInput2) + const output2 = urlRegex.test(mockInput2) expect(output2).toBe(false) const mockInput4 = 'hello world.com' - const output4 = domainRegex.test(mockInput4) + const output4 = urlRegex.test(mockInput4) expect(output4).toBe(false) const mockInput5 = 'email@domain.com' - const output5 = domainRegex.test(mockInput5) + const output5 = urlRegex.test(mockInput5) expect(output5).toBe(false) }) }) From f83bd85f1d2c8b31d99062e21a699880540be186 Mon Sep 17 00:00:00 2001 From: gitstart Date: Fri, 25 Nov 2022 16:12:56 +0000 Subject: [PATCH 02/47] fix: rename/remove table filter/sort rules on column rename --- .../SidePanelEditor/SidePanelEditor.tsx | 82 ++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx index 8a0d3ee57d3..c04a98fff2a 100644 --- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx +++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx @@ -9,11 +9,13 @@ import { PostgresType, } from '@supabase/postgres-meta' -import { useStore } from 'hooks' +import { useStore, useUrlState } from 'hooks' import { RowEditor, ColumnEditor, TableEditor } from '.' import { ImportContent } from './TableEditor/TableEditor.types' import { ColumnField, CreateColumnPayload, UpdateColumnPayload } from './SidePanelEditor.types' import ConfirmationModal from 'components/ui/ConfirmationModal' +import { onLoadStorage, saveStorageDebounced } from 'components/grid/SupabaseGrid.utils' +import { InitialStateType } from 'components/grid/store/reducers' interface Props { selectedSchema: string @@ -48,9 +50,11 @@ const SidePanelEditor: FC = ({ onColumnSaved = () => {}, }) => { const { meta, ui } = useStore() + const projectRef = ui.selectedProject?.ref const [isEdited, setIsEdited] = useState(false) const [isClosingPanel, setIsClosingPanel] = useState(false) + const [_, setParams] = useUrlState({ arrayKeys: ['filter', 'sort'] }) const tables = meta.tables.list() const enumTypes = meta.types.list( @@ -121,6 +125,65 @@ const SidePanelEditor: FC = ({ } } + /** + * Renames a column's filter and/or sort rules if the related column has been renamed. + */ + const updateSortsAndFiltersAfterColumnRename = (oldColumnName: string, newColumnName: string) => { + setParams((prevParams) => { + const existingFilters = (prevParams?.filter ?? []) as string[] + const existingSorts = (prevParams?.sort ?? []) as string[] + + return { + ...prevParams, + filter: existingFilters.map((filter: string) => { + const [column] = filter.split(':') + return column === oldColumnName ? filter.replace(column, newColumnName) : filter + }), + sort: existingSorts.map((sort: string) => { + const [column] = sort.split(':') + return column === oldColumnName ? sort.replace(column, newColumnName) : sort + }), + } + }) + } + + /** + * Removes tables's column filter and/or sort rules if the related column unavailable + * (resulting from name change or deleted column) + */ + const updateSavedStateAfterTableUpdate = ( + storageRef: string, + tableName: string, + schema: string | null, + tableColumns: ColumnField[] + ) => { + const savedState = onLoadStorage(storageRef, tableName, schema) + const columnNames = tableColumns.map((columnInstance) => columnInstance.name) + const { filters, sorts }: { filters?: string[]; sorts?: string[] } = savedState + const state = { + table: { name: tableName, schema }, + gridColumns: savedState?.gridColumns, + } as InitialStateType + + const newSorts = sorts?.filter((filterQuery) => { + const [column] = filterQuery.split(':') + + return columnNames.includes(column) + }) + const newFilters = filters?.filter((sortrQuery) => { + const [column] = sortrQuery.split(':') + + return columnNames.includes(column) + }) + + saveStorageDebounced( + state, + storageRef, + newSorts?.length ? newSorts : undefined, + newFilters?.length ? newFilters : undefined + ) + } + const saveColumn = async ( payload: CreateColumnPayload | UpdateColumnPayload, foreignKey: Partial | undefined, @@ -144,6 +207,15 @@ const SidePanelEditor: FC = ({ if (response?.error) { ui.setNotification({ category: 'error', message: response.error.message }) } else { + if ( + !isNewRecord && + payload.name && + selectedColumnToEdit && + selectedColumnToEdit.name !== payload.name + ) { + updateSortsAndFiltersAfterColumnRename(selectedColumnToEdit.name, payload.name) + } + await meta.tables.loadById(selectedTable!.id) onColumnSaved() setIsEdited(false) @@ -235,6 +307,14 @@ const SidePanelEditor: FC = ({ message: `Successfully updated ${table.name}!`, }) } + if (projectRef) { + updateSavedStateAfterTableUpdate( + projectRef, + selectedTableToEdit.name, + selectedTableToEdit.schema, + columns + ) + } } } catch (error: any) { saveTableError = true From c2d0825d42b2867a7dea8754bff9040ad1a02f70 Mon Sep 17 00:00:00 2001 From: gitstart Date: Mon, 26 Dec 2022 17:24:31 +0000 Subject: [PATCH 03/47] chore: remove unavailable columns from sorts and filters --- studio/components/grid/SupabaseGrid.tsx | 30 +++++++ studio/components/grid/utils/row.ts | 15 +++- .../SidePanelEditor/SidePanelEditor.tsx | 82 +------------------ 3 files changed, 43 insertions(+), 84 deletions(-) diff --git a/studio/components/grid/SupabaseGrid.tsx b/studio/components/grid/SupabaseGrid.tsx index 75b3abf65b9..c454b675216 100644 --- a/studio/components/grid/SupabaseGrid.tsx +++ b/studio/components/grid/SupabaseGrid.tsx @@ -95,6 +95,36 @@ const SupabaseGridLayout = forwardRef((props }, []) useEffect(() => { + if (state.refreshPageFlag == 0) { + return + } + + const columnNames = state.table?.columns.map((column) => column.name) + + if ( + columnNames && + (sorts.some((sortInstance) => !columnNames.includes(sortInstance.column)) || + filters.some((filterInstance) => !columnNames.includes(filterInstance.column))) + ) { + // Remove unavailable columns from "sort" and "filter" query string parameters + setParams((prevParams) => { + return { + ...prevParams, + filter: filters + .filter((filterInstance) => columnNames.includes(filterInstance.column)) + .map( + (filterInstance) => + `${filterInstance.column}:${filterInstance.operator}:${filterInstance.value}` + ), + sort: sorts + .filter((sortInstance) => columnNames.includes(sortInstance.column)) + .map( + (sortInstance) => `${sortInstance.column}:${sortInstance.ascending ? 'asc' : 'desc'}` + ), + } + }) + } + if (state.refreshPageFlag === REFRESH_PAGE_IMMEDIATELY) { fetchPage(state, dispatch, sorts, filters) } else if (state.refreshPageFlag !== 0) { diff --git a/studio/components/grid/utils/row.ts b/studio/components/grid/utils/row.ts index aabf2c85a78..59f43166ee4 100644 --- a/studio/components/grid/utils/row.ts +++ b/studio/components/grid/utils/row.ts @@ -9,7 +9,11 @@ export async function fetchCount( ) { if (!state.rowService) return - const { data, error } = await state.rowService.count(filters) + // Remove unavailable columns from filters + const columnNames = state.table?.columns.map((column) => column.name) ?? [] + const cleanedFilters = filters.filter((filter) => columnNames.includes(filter.column)) + + const { data, error } = await state.rowService.count(cleanedFilters) if (error) { // TODO: handle fetch rows count error } else { @@ -35,11 +39,16 @@ export async function fetchPage( dispatch({ type: 'SET_IS_LOADING', payload: { isLoading: true } }) + // Remove unavailable columns from sorts & filter + const columnNames = state.table?.columns.map((column) => column.name) ?? [] + const cleanedFilters = filters.filter((filter) => columnNames.includes(filter.column)) + const cleanedSorts = sorts.filter((sort) => columnNames.includes(sort.column)) + const { data, error } = await state.rowService.fetchPage( state.page, state.rowsPerPage, - filters, - sorts + cleanedFilters, + cleanedSorts ) if (error) { // TODO: handle fetch rows data error diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx index b0b8efcac56..7e410f7d2ab 100644 --- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx +++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx @@ -4,13 +4,11 @@ import { Query, Dictionary } from 'components/grid' import { Modal } from 'ui' import { PostgresRelationship, PostgresTable, PostgresColumn } from '@supabase/postgres-meta' -import { useStore, useUrlState } from 'hooks' +import { useStore } from 'hooks' import { RowEditor, ColumnEditor, TableEditor } from '.' import { ImportContent } from './TableEditor/TableEditor.types' import { ColumnField, CreateColumnPayload, UpdateColumnPayload } from './SidePanelEditor.types' import ConfirmationModal from 'components/ui/ConfirmationModal' -import { onLoadStorage, saveStorageDebounced } from 'components/grid/SupabaseGrid.utils' -import { InitialStateType } from 'components/grid/store/reducers' interface Props { selectedSchema: string @@ -45,11 +43,9 @@ const SidePanelEditor: FC = ({ onColumnSaved = () => {}, }) => { const { meta, ui } = useStore() - const projectRef = ui.selectedProject?.ref const [isEdited, setIsEdited] = useState(false) const [isClosingPanel, setIsClosingPanel] = useState(false) - const [_, setParams] = useUrlState({ arrayKeys: ['filter', 'sort'] }) const tables = meta.tables.list() @@ -117,65 +113,6 @@ const SidePanelEditor: FC = ({ } } - /** - * Renames a column's filter and/or sort rules if the related column has been renamed. - */ - const updateSortsAndFiltersAfterColumnRename = (oldColumnName: string, newColumnName: string) => { - setParams((prevParams) => { - const existingFilters = (prevParams?.filter ?? []) as string[] - const existingSorts = (prevParams?.sort ?? []) as string[] - - return { - ...prevParams, - filter: existingFilters.map((filter: string) => { - const [column] = filter.split(':') - return column === oldColumnName ? filter.replace(column, newColumnName) : filter - }), - sort: existingSorts.map((sort: string) => { - const [column] = sort.split(':') - return column === oldColumnName ? sort.replace(column, newColumnName) : sort - }), - } - }) - } - - /** - * Removes tables's column filter and/or sort rules if the related column unavailable - * (resulting from name change or deleted column) - */ - const updateSavedStateAfterTableUpdate = ( - storageRef: string, - tableName: string, - schema: string | null, - tableColumns: ColumnField[] - ) => { - const savedState = onLoadStorage(storageRef, tableName, schema) - const columnNames = tableColumns.map((columnInstance) => columnInstance.name) - const { filters, sorts }: { filters?: string[]; sorts?: string[] } = savedState - const state = { - table: { name: tableName, schema }, - gridColumns: savedState?.gridColumns, - } as InitialStateType - - const newSorts = sorts?.filter((filterQuery) => { - const [column] = filterQuery.split(':') - - return columnNames.includes(column) - }) - const newFilters = filters?.filter((sortrQuery) => { - const [column] = sortrQuery.split(':') - - return columnNames.includes(column) - }) - - saveStorageDebounced( - state, - storageRef, - newSorts?.length ? newSorts : undefined, - newFilters?.length ? newFilters : undefined - ) - } - const saveColumn = async ( payload: CreateColumnPayload | UpdateColumnPayload, foreignKey: Partial | undefined, @@ -201,15 +138,6 @@ const SidePanelEditor: FC = ({ if (response?.error) { ui.setNotification({ category: 'error', message: response.error.message }) } else { - if ( - !isNewRecord && - payload.name && - selectedColumnToEdit && - selectedColumnToEdit.name !== payload.name - ) { - updateSortsAndFiltersAfterColumnRename(selectedColumnToEdit.name, payload.name) - } - await meta.tables.loadById(selectedTable!.id) onColumnSaved(configuration.isEncrypted) setIsEdited(false) @@ -305,14 +233,6 @@ const SidePanelEditor: FC = ({ message: `Successfully updated ${table.name}!`, }) } - if (projectRef) { - updateSavedStateAfterTableUpdate( - projectRef, - selectedTableToEdit.name, - selectedTableToEdit.schema, - columns - ) - } } } catch (error: any) { saveTableError = true From de0082e8006ad03b20737dc5bc531fb257be3cc2 Mon Sep 17 00:00:00 2001 From: Akos <65110142+projectaki@users.noreply.github.com> Date: Mon, 2 Jan 2023 18:48:05 +0900 Subject: [PATCH 04/47] Update with-expo.mdx Instead of AsyncStorage use expos SecureStorage API. This is the recommended place to store sensitive information such as public client auth details. The storage key in the auth config of the supabase client expects: setItem, getItem and removeItem so a simple adapter can be written for SecureStore. --- .../getting-started/tutorials/with-expo.mdx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx index b60db313a51..e5e150532f8 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx @@ -38,6 +38,7 @@ Then let's install the additional dependencies: [supabase-js](https://github.com ```bash npm install @supabase/supabase-js npm install react-native-elements @react-native-async-storage/async-storage react-native-url-polyfill +npx expo install expo-secure-store ``` Now let's create a helper file to initialize the Supabase client. @@ -46,15 +47,27 @@ These variables will be exposed on the browser, and that's completely fine since [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database. ```ts title=lib/supabase.ts -import AsyncStorage from '@react-native-async-storage/async-storage' +import * as SecureStore from "expo-secure-store"; import { createClient } from '@supabase/supabase-js' +const ExpoSecureStoreAdapter = { + getItem: async (key: string) => { + return await SecureStore.getItemAsync(key); + }, + setItem: async (key: string, value: string) => { + SecureStore.setItemAsync(key, JSON.stringify(value)); + }, + removeItem: async (key: string) => { + await SecureStore.deleteItemAsync(key); + }, +}; + const supabaseUrl = YOUR_REACT_NATIVE_SUPABASE_URL const supabaseAnonKey = YOUR_REACT_NATIVE_SUPABASE_ANON_KEY export const supabase = createClient(supabaseUrl, supabaseAnonKey, { auth: { - storage: AsyncStorage as any, + storage: ExpoSecureStoreAdapter as any, autoRefreshToken: true, persistSession: true, detectSessionInUrl: false, From a335a47136fa17e34611ad137523400e3b0b8f83 Mon Sep 17 00:00:00 2001 From: Akos Date: Mon, 2 Jan 2023 22:44:45 +0900 Subject: [PATCH 05/47] fix: parse the return value into an object Go true client expects an object as the returned value, and the session will not be restored otherwise --- .../guides/getting-started/tutorials/with-expo.mdx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx index e5e150532f8..bcbe40c50cd 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx @@ -50,15 +50,17 @@ These variables will be exposed on the browser, and that's completely fine since import * as SecureStore from "expo-secure-store"; import { createClient } from '@supabase/supabase-js' -const ExpoSecureStoreAdapter = { +const ExpoSecureStorageAdapter = { getItem: async (key: string) => { - return await SecureStore.getItemAsync(key); + const value = (await SecureStore.getItemAsync(key)) as string; + + return JSON.parse(value); }, - setItem: async (key: string, value: string) => { + setItem: (key: string, value: string) => { SecureStore.setItemAsync(key, JSON.stringify(value)); }, - removeItem: async (key: string) => { - await SecureStore.deleteItemAsync(key); + removeItem: (key: string) => { + SecureStore.deleteItemAsync(key); }, }; From 7c1054c0817322660a940ba0e63d8570db7fafe8 Mon Sep 17 00:00:00 2001 From: Akos Date: Mon, 2 Jan 2023 23:08:20 +0900 Subject: [PATCH 06/47] fix: remove manual JSON parse/stringify, handled by GoTrue --- .../pages/guides/getting-started/tutorials/with-expo.mdx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx index bcbe40c50cd..6541bf460dc 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx @@ -51,13 +51,11 @@ import * as SecureStore from "expo-secure-store"; import { createClient } from '@supabase/supabase-js' const ExpoSecureStorageAdapter = { - getItem: async (key: string) => { - const value = (await SecureStore.getItemAsync(key)) as string; - - return JSON.parse(value); + getItem: (key: string) => { + return SecureStore.getItemAsync(key); }, setItem: (key: string, value: string) => { - SecureStore.setItemAsync(key, JSON.stringify(value)); + SecureStore.setItemAsync(key, value); }, removeItem: (key: string) => { SecureStore.deleteItemAsync(key); From c480be7e791f3ddeeda94133386365557db146cb Mon Sep 17 00:00:00 2001 From: gitstart Date: Fri, 13 Jan 2023 05:18:47 +0000 Subject: [PATCH 07/47] chore: hide subscription in views --- .../interfaces/Docs/ResourceContent.tsx | 89 ++++++++++--------- studio/pages/project/[ref]/api/index.tsx | 13 +++ 2 files changed, 59 insertions(+), 43 deletions(-) diff --git a/studio/components/interfaces/Docs/ResourceContent.tsx b/studio/components/interfaces/Docs/ResourceContent.tsx index a37ed884081..6fc63ae6b95 100644 --- a/studio/components/interfaces/Docs/ResourceContent.tsx +++ b/studio/components/interfaces/Docs/ResourceContent.tsx @@ -12,6 +12,7 @@ const ResourceContent = ({ selectedLang, showApiKey, refreshDocs, + isView, }: any) => { if (!paths || !definitions) return null @@ -221,49 +222,51 @@ const ResourceContent = ({ )} - <> -

Subscribe to changes

-
-
-

- Supabase provides realtime functionality and broadcasts database changes to authorized - users depending on Row Level Security (RLS) policies. -

-

- - Learn more. - -

-
-
- - - - - -
-
- + {!isView && ( + <> +

Subscribe to changes

+
+
+

+ Supabase provides realtime functionality and broadcasts database changes to + authorized users depending on Row Level Security (RLS) policies. +

+

+ + Learn more. + +

+
+
+ + + + + +
+
+ + )} <>

Much more

diff --git a/studio/pages/project/[ref]/api/index.tsx b/studio/pages/project/[ref]/api/index.tsx index 482494908ff..a4ac6b6cc21 100644 --- a/studio/pages/project/[ref]/api/index.tsx +++ b/studio/pages/project/[ref]/api/index.tsx @@ -75,6 +75,7 @@ const DocView: FC = observer(({}) => { const API_KEY = data?.autoApiService?.defaultApiKey const swaggerUrl = data?.autoApiService?.restUrl const headers: any = { apikey: API_KEY } + const selectedSchema = data?.autoApiService?.app_config?.db_schema const canReadServiceKey = checkPermissions( PermissionAction.READ, @@ -88,10 +89,19 @@ const DocView: FC = observer(({}) => { (url: string) => get(url, { headers, credentials: 'omit' }).then((res) => res) ) + const { meta } = useStore() + useEffect(() => { PageState.setJsonSchema(jsonSchema) }, [jsonSchema]) + useEffect(() => { + const fetchViews = async (selectedSchema: string) => { + await meta.schemas.loadViews(selectedSchema) + } + if (selectedSchema) fetchViews(selectedSchema) + }, [selectedSchema]) + const refreshDocs = async () => { // A bit hacky calling coding this up twice - at some point we should move this function // and the SWR into the store. @@ -128,6 +138,8 @@ const DocView: FC = observer(({}) => { const PAGE_KEY: any = resource || rpc || page || 'index' + const resourceIsView = meta.schemas.views?.some((view) => view.name === resource) + return (
@@ -216,6 +228,7 @@ const DocView: FC = observer(({}) => { paths={paths} showApiKey={showApiKey.key} refreshDocs={refreshDocs} + isView={resourceIsView} /> ) : rpc ? ( Date: Fri, 27 Jan 2023 21:22:42 +0900 Subject: [PATCH 08/47] fix: typo --- apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx index 6541bf460dc..d141a8b1321 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-expo.mdx @@ -50,7 +50,7 @@ These variables will be exposed on the browser, and that's completely fine since import * as SecureStore from "expo-secure-store"; import { createClient } from '@supabase/supabase-js' -const ExpoSecureStorageAdapter = { +const ExpoSecureStoreAdapter = { getItem: (key: string) => { return SecureStore.getItemAsync(key); }, From 72b716eb22dbf704d1d412e7f12ec705616dc4aa Mon Sep 17 00:00:00 2001 From: gitstart Date: Thu, 2 Feb 2023 09:46:37 +0000 Subject: [PATCH 09/47] chore: support column name update with active filter --- studio/components/grid/store/reducers/base.ts | 8 ++--- .../SidePanelEditor/SidePanelEditor.tsx | 34 ++++++++++++++++++- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/studio/components/grid/store/reducers/base.ts b/studio/components/grid/store/reducers/base.ts index 1307eb23007..9795680c2df 100644 --- a/studio/components/grid/store/reducers/base.ts +++ b/studio/components/grid/store/reducers/base.ts @@ -83,15 +83,15 @@ const BaseReducer = (state: BaseInitialState, action: BASE_ACTIONTYPE) => { case 'UPDATE_FILTERS': { const newState: any = { ...state } newState.page = 1 - // newState.refreshPageFlag = Date.now() - newState.refreshPageFlag = REFRESH_PAGE_IMMEDIATELY + newState.refreshPageFlag = Date.now() + // newState.refreshPageFlag = REFRESH_PAGE_IMMEDIATELY newState.totalRows = TOTAL_ROWS_RESET return newState } case 'UPDATE_SORTS': { const newState: any = { ...state } - // newState.refreshPageFlag = Date.now() - newState.refreshPageFlag = REFRESH_PAGE_IMMEDIATELY + newState.refreshPageFlag = Date.now() + // newState.refreshPageFlag = REFRESH_PAGE_IMMEDIATELY return newState } default: diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx index 7d8c40bd8d0..23dc0ebb37c 100644 --- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx +++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx @@ -4,7 +4,7 @@ import { Query, Dictionary } from 'components/grid' import { Modal } from 'ui' import type { PostgresRelationship, PostgresTable, PostgresColumn } from '@supabase/postgres-meta' -import { useStore } from 'hooks' +import { useStore, useUrlState } from 'hooks' import { RowEditor, ColumnEditor, TableEditor } from '.' import { ImportContent } from './TableEditor/TableEditor.types' import { ColumnField, CreateColumnPayload, UpdateColumnPayload } from './SidePanelEditor.types' @@ -42,6 +42,7 @@ const SidePanelEditor: FC = ({ onTableCreated = () => {}, onColumnSaved = () => {}, }) => { + const [_, setParams] = useUrlState({ arrayKeys: ['filter', 'sort'] }) const { meta, ui } = useStore() const [isEdited, setIsEdited] = useState(false) @@ -139,6 +140,15 @@ const SidePanelEditor: FC = ({ ui.setNotification({ category: 'error', message: response.error.message }) } else { await meta.tables.loadById(selectedTable!.id) + if ( + !isNewRecord && + payload.name && + selectedColumnToEdit && + selectedColumnToEdit.name !== payload.name + ) { + reAddRenamedColumnSortAndFilter(selectedColumnToEdit.name, payload.name) + } + onColumnSaved(configuration.isEncrypted) setIsEdited(false) closePanel() @@ -151,6 +161,28 @@ const SidePanelEditor: FC = ({ resolve() } + /** + * Adds the renamed column's filter and/or sort rules. + */ + const reAddRenamedColumnSortAndFilter = (oldColumnName: string, newColumnName: string) => { + setParams((prevParams) => { + const existingFilters = (prevParams?.filter ?? []) as string[] + const existingSorts = (prevParams?.sort ?? []) as string[] + + return { + ...prevParams, + filter: existingFilters.map((filter: string) => { + const [column] = filter.split(':') + return column === oldColumnName ? filter.replace(column, newColumnName) : filter + }), + sort: existingSorts.map((sort: string) => { + const [column] = sort.split(':') + return column === oldColumnName ? sort.replace(column, newColumnName) : sort + }), + } + }) + } + const saveTable = async ( payload: any, columns: ColumnField[], From 4e36eb80f84020048848f3adb3cb82af750ce6b2 Mon Sep 17 00:00:00 2001 From: Liau Jian Jie Date: Sat, 18 Feb 2023 21:27:20 +0800 Subject: [PATCH 10/47] Docs: remove unused `session` variable Removes the destructured `session` variable from `useSupabase()`. This variable is never referenced and is not returned as a property from `useSupabase()`. --- .../guides/auth/auth-helpers/nextjs-server-components.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/docs/pages/guides/auth/auth-helpers/nextjs-server-components.mdx b/apps/docs/pages/guides/auth/auth-helpers/nextjs-server-components.mdx index a31afc3d055..a2bae56dc30 100644 --- a/apps/docs/pages/guides/auth/auth-helpers/nextjs-server-components.mdx +++ b/apps/docs/pages/guides/auth/auth-helpers/nextjs-server-components.mdx @@ -506,7 +506,7 @@ import { useSupabase } from './supabase-provider' // Supabase auth needs to be triggered client-side export default function Login() { - const { supabase, session } = useSupabase() + const { supabase } = useSupabase() const handleEmailLogin = async () => { await supabase.auth.signInWithPassword({ @@ -546,7 +546,7 @@ import { useSupabase } from './supabase-provider' // Supabase auth needs to be triggered client-side export default function Login() { - const { supabase, session } = useSupabase() + const { supabase } = useSupabase() const handleEmailLogin = async () => { await supabase.auth.signInWithPassword({ From d1634e7e6675fe6c4e2b341c4c4dc89ee47b976c Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Wed, 1 Mar 2023 15:31:54 +0100 Subject: [PATCH 11/47] Adds a react quickstart --- .../NavigationMenu.constants.ts | 4 + .../pages/guides/auth/quickstarts/react.mdx | 133 ++++++++++++++++++ package-lock.json | 2 +- 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 apps/docs/pages/guides/auth/quickstarts/react.mdx diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 11043ca46c4..0d088c00d05 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -292,6 +292,10 @@ export const auth = { name: 'Overview', url: '/guides/auth/overview', }, + { + name: 'Quickstarts', + items: [{ name: 'Auth with React', url: '/guides/auth/quickstarts/react', items: [] }], + }, { name: 'Authentication', url: undefined, diff --git a/apps/docs/pages/guides/auth/quickstarts/react.mdx b/apps/docs/pages/guides/auth/quickstarts/react.mdx new file mode 100644 index 00000000000..f47e374a48c --- /dev/null +++ b/apps/docs/pages/guides/auth/quickstarts/react.mdx @@ -0,0 +1,133 @@ +import Layout from '~/layouts/DefaultGuideLayout' +import StepHikeCompact from '~/components/StepHikeCompact' + +export const meta = { + title: 'Use Supabase Auth with React', + subtitle: 'Learn how to Supabase Auth with React.js.', + breadcrumb: 'Auth Quickstarts', +} + + + + + + + [Launch a new project](https://app.supabase.com) in the Supabase Dashboard. + + Your new database has a table for storing your users. You can see that this table is currently empty by running some SQL in the [SQL Editor](https://app.supabase.com/project/_/sql). + + + + + + ```sql SQL_EDITOR + select * from auth.users; + ```` + + + + + + + + + + Create a React app using the `create-react-app` command. + + + + + + ```bash Terminal + npx create-react-app my-app + ``` + + + + + + + + + The fastest way to get started is to use Supabase's `auth-ui-react` library which provides a convenient interface for working with Supabase Auth from a React app. + + Navigate to the React app and install the Supabase libraries. + + + + + + ```bash Terminal + cd my-app && npm install @supabase/supabase-js @supabase/auth-ui-react + ``` + + + + + + + + + In `index.js`, create a Supabase client using your [Project URL and public API (anon) key](https://app.supabase.com/project/_/settings/api). + + You can configure the Auth component to display whenever there is no session inside `supabase.auth.getSession()` + + + + + + ```js src/index.js + import './index.css' + import { useState, useEffect } from 'react' + import { createClient } from '@supabase/supabase-js' + import { Auth, ThemeSupa } from '@supabase/auth-ui-react' + + const supabase = createClient('https://.supabase.co', '') + + export default function App() { + const [session, setSession] = useState(null) + + useEffect(() => { + supabase.auth.getSession().then(({ data: { session } }) => { + setSession(session) + }) + + supabase.auth.onAuthStateChange((_event, session) => { + setSession(session) + }) + }, []) + + if (!session) { + return () + } + else { + return (
Logged in!
) + } + } + ``` + +
+ +
+ + + + + Start the app, go to http://localhost:3000 in a browser, and open the browser console and you should be able to log in. + + + + + + ```bash Terminal + npm start + ``` + + + + +
+ +export const Page = ({ children }) => + +export default Page diff --git a/package-lock.json b/package-lock.json index dbf8afff773..fa926877ce0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46189,7 +46189,7 @@ "@sinclair/typebox": "^0.25.1", "pg": "^8.7.1", "pg-format": "^1.0.4", - "pgsql-parser": "13.4.0", + "pgsql-parser": "^13.3.0", "postgres-array": "^3.0.1", "prettier": "^2.6.0", "prettier-plugin-sql": "^0.12.1" From 13cf232ceda02cc874660f7b07481f0fbdd0e613 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Wed, 1 Mar 2023 15:39:51 +0100 Subject: [PATCH 12/47] Adds sidebar (again?) --- apps/docs/components/Navigation/Navigation.constants.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/docs/components/Navigation/Navigation.constants.ts b/apps/docs/components/Navigation/Navigation.constants.ts index 94f40fce1ee..ade5cc58244 100644 --- a/apps/docs/components/Navigation/Navigation.constants.ts +++ b/apps/docs/components/Navigation/Navigation.constants.ts @@ -80,6 +80,7 @@ export const menuItems: NavMenu = { label: 'Auth', items: [ { name: 'Overview', url: '/guides/auth', items: [] }, + { name: 'Quickstarts', url: '/guides/auth/quickstarts/react', items: [] }, { name: 'Authentication', url: undefined, From b8714805dec7aef5222cf2b52dea05c950aac347 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 9 Mar 2023 22:46:41 +0700 Subject: [PATCH 13/47] Update server-side-rendering.mdx Fix "AuthApiError: invalid claim: missing sub claim" from the server side. --- apps/docs/pages/guides/auth/server-side-rendering.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/docs/pages/guides/auth/server-side-rendering.mdx b/apps/docs/pages/guides/auth/server-side-rendering.mdx index 346984a63fb..c5abc72a4e3 100644 --- a/apps/docs/pages/guides/auth/server-side-rendering.mdx +++ b/apps/docs/pages/guides/auth/server-side-rendering.mdx @@ -130,6 +130,11 @@ if (refreshToken && accessToken) { await supabase.auth.setSession({ refresh_token: refreshToken, access_token: accessToken, + { + auth: { + persistSession: false, + }, + } }) } else { // make sure you handle this case! From 302f1252945e110b98f22faaf7f8edd6ce09dbad Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Thu, 23 Mar 2023 20:15:48 +0800 Subject: [PATCH 14/47] Took a surprising amount of effort to get to here. Move the Serverless API docs into their own section --- .../Navigation/Navigation.constants.ts | 15 +++++++-------- .../Navigation/NavigationMenu/HomeMenu.tsx | 6 ++++++ .../NavigationMenu/NavigationMenu.constants.ts | 18 ++++++++++-------- .../NavigationMenu/NavigationMenu.tsx | 5 +++++ apps/docs/layouts/SiteLayout.tsx | 4 ++++ apps/docs/pages/guides/{database => }/api.mdx | 0 .../{database => }/api/generating-types.mdx | 0 7 files changed, 32 insertions(+), 16 deletions(-) rename apps/docs/pages/guides/{database => }/api.mdx (100%) rename apps/docs/pages/guides/{database => }/api/generating-types.mdx (100%) diff --git a/apps/docs/components/Navigation/Navigation.constants.ts b/apps/docs/components/Navigation/Navigation.constants.ts index beb5bdc962d..a4fb05f3658 100644 --- a/apps/docs/components/Navigation/Navigation.constants.ts +++ b/apps/docs/components/Navigation/Navigation.constants.ts @@ -174,14 +174,6 @@ export const menuItems: NavMenu = { { name: 'Database Webhooks', url: '/guides/database/webhooks', items: [] }, { name: 'Full Text Search', url: '/guides/database/full-text-search', items: [] }, { name: 'Database Testing', url: '/guides/database/testing', items: [] }, - { - name: 'Serverless APIs', - url: undefined, - items: [ - { name: 'Overview', url: '/guides/database/api', items: [] }, - { name: 'Generating Types', url: '/guides/database/api/generating-types', items: [] }, - ], - }, { name: 'Extensions', url: undefined, @@ -228,6 +220,13 @@ export const menuItems: NavMenu = { }, ], }, + { + label: 'Serverless APIs', + items: [ + { name: 'Overview', url: '/guides/api', items: [] }, + { name: 'Generating Types', url: '/guides/api/generating-types', items: [] }, + ], + }, { label: 'Edge Functions', items: [ diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx index 36f01bf6830..991adea0ef6 100644 --- a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx @@ -27,6 +27,12 @@ const home = [ href: '/guides/database', level: 'database', }, + { + label: 'Serverless APIs', + icon: '/img/icons/menu/database', + href: '/guides/api', + level: 'api', + }, { label: 'Auth', icon: '/img/icons/menu/auth', diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 240e34fdf32..c8232e61e26 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -386,14 +386,6 @@ export const database = { { name: 'Database Webhooks', url: '/guides/database/webhooks', items: [] }, { name: 'Full Text Search', url: '/guides/database/full-text-search', items: [] }, { name: 'Database Testing', url: '/guides/database/testing', items: [] }, - { - name: 'Serverless APIs', - url: undefined, - items: [ - { name: 'Overview', url: '/guides/database/api', items: [] }, - { name: 'Generating Types', url: '/guides/database/api/generating-types', items: [] }, - ], - }, { name: 'Extensions', url: undefined, @@ -558,6 +550,16 @@ export const database = { ], } +export const api = { + icon: 'database', + label: 'Serverless APIs', + url: '/guides/api', + items: [ + { name: 'Overview', url: '/guides/api', items: [] }, + { name: 'Generating Types', url: '/guides/api/generating-types', items: [] }, + ], +} + export const functions = { icon: 'functions', label: 'Edge Functions', diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx index e585077975f..0b481973cb8 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx @@ -85,6 +85,9 @@ const NavigationMenu = () => { case url.includes(`/docs/guides/database`) && url: menuState.setMenuLevelId('database') break + case url.includes(`/docs/guides/api`) && url: + menuState.setMenuLevelId('api') + break case url.includes(`/docs/guides/auth`) && url: menuState.setMenuLevelId('auth') break @@ -174,6 +177,7 @@ const NavigationMenu = () => { const isHomeActive = 'home' === level const isGettingStartedActive = 'gettingstarted' === level const isDatabaseActive = 'database' === level + const isApiActive = 'api' === level const isAuthActive = 'auth' === level const isFunctionsActive = 'functions' === level const isRealtimeActive = 'realtime' === level @@ -203,6 +207,7 @@ const NavigationMenu = () => { + Date: Thu, 23 Mar 2023 20:36:47 +0800 Subject: [PATCH 15/47] stubs out a few different guides --- .../NavigationMenu/NavigationMenu.constants.ts | 17 ++++++++++++++++- apps/docs/pages/guides/api/graphql/graphiql.mdx | 14 ++++++++++++++ .../guides/api/rest/auto-generated-docs.mdx | 14 ++++++++++++++ apps/docs/pages/guides/api/rest/caching.mdx | 16 ++++++++++++++++ apps/docs/pages/guides/api/rest/client-libs.mdx | 14 ++++++++++++++ .../guides/api/{ => rest}/generating-types.mdx | 0 .../pages/guides/api/rest/joins-and-nesting.mdx | 14 ++++++++++++++ 7 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 apps/docs/pages/guides/api/graphql/graphiql.mdx create mode 100644 apps/docs/pages/guides/api/rest/auto-generated-docs.mdx create mode 100644 apps/docs/pages/guides/api/rest/caching.mdx create mode 100644 apps/docs/pages/guides/api/rest/client-libs.mdx rename apps/docs/pages/guides/api/{ => rest}/generating-types.mdx (100%) create mode 100644 apps/docs/pages/guides/api/rest/joins-and-nesting.mdx diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index c8232e61e26..441c7ca2aab 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -556,7 +556,22 @@ export const api = { url: '/guides/api', items: [ { name: 'Overview', url: '/guides/api', items: [] }, - { name: 'Generating Types', url: '/guides/api/generating-types', items: [] }, + { + name: 'REST', + url: undefined, + items: [ + { name: 'Client Libraries', url: '/guides/api/rest/client-libs', items: [] }, + { name: 'Generating Types', url: '/guides/api/rest/generating-types', items: [] }, + { name: 'Auto-generated Docs', url: '/guides/api/rest/auto-generated-docs', items: [] }, + { name: 'Joins and Nested Tables', url: '/guides/api/rest/joins-and-nesting', items: [] }, + { name: 'Caching', url: '/guides/api/rest/caching', items: [] }, + ], + }, + { + name: 'GRAPHQL', + url: undefined, + items: [{ name: 'GraphiQL', url: '/guides/api/graphql/graphiql', items: [] }], + }, ], } diff --git a/apps/docs/pages/guides/api/graphql/graphiql.mdx b/apps/docs/pages/guides/api/graphql/graphiql.mdx new file mode 100644 index 00000000000..9887e72278f --- /dev/null +++ b/apps/docs/pages/guides/api/graphql/graphiql.mdx @@ -0,0 +1,14 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'graphiql', + title: 'GraphiQL documentation', + description: 'Some docs on graphiql.', + video: 'https://www.youtube.com/v/7CqlTU9aOR4', +} + +GraphiQL + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/api/rest/auto-generated-docs.mdx b/apps/docs/pages/guides/api/rest/auto-generated-docs.mdx new file mode 100644 index 00000000000..bde5bb7bb21 --- /dev/null +++ b/apps/docs/pages/guides/api/rest/auto-generated-docs.mdx @@ -0,0 +1,14 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'auto-docs', + title: 'Auto-generated documentation', + description: 'Suapbase provides several client libraries for the REST interface.', + video: 'https://www.youtube.com/v/7CqlTU9aOR4', +} + +A doc on the Client Libraries, how they are structured, and where to find the docs. + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/api/rest/caching.mdx b/apps/docs/pages/guides/api/rest/caching.mdx new file mode 100644 index 00000000000..fe437d7d961 --- /dev/null +++ b/apps/docs/pages/guides/api/rest/caching.mdx @@ -0,0 +1,16 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'joins-and-nested-tables', + title: 'Joins and Nested tables', + description: 'A description.', + video: 'https://www.youtube.com/v/7CqlTU9aOR4', +} + +- Supabase cache helpers: https://github.com/psteinroe/supabase-cache-helpers +- SWR +- React Query + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/api/rest/client-libs.mdx b/apps/docs/pages/guides/api/rest/client-libs.mdx new file mode 100644 index 00000000000..0f5c374074d --- /dev/null +++ b/apps/docs/pages/guides/api/rest/client-libs.mdx @@ -0,0 +1,14 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'client-libs', + title: 'Client Libraries', + description: 'Suapbase provides several client libraries for the REST interface.', + video: 'https://www.youtube.com/v/7CqlTU9aOR4', +} + +A doc on the Client Libraries, how they are structured, and where to find the docs. + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/api/generating-types.mdx b/apps/docs/pages/guides/api/rest/generating-types.mdx similarity index 100% rename from apps/docs/pages/guides/api/generating-types.mdx rename to apps/docs/pages/guides/api/rest/generating-types.mdx diff --git a/apps/docs/pages/guides/api/rest/joins-and-nesting.mdx b/apps/docs/pages/guides/api/rest/joins-and-nesting.mdx new file mode 100644 index 00000000000..f0a3abc4c98 --- /dev/null +++ b/apps/docs/pages/guides/api/rest/joins-and-nesting.mdx @@ -0,0 +1,14 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'joins-and-nested-tables', + title: 'Joins and Nested tables', + description: 'A description.', + video: 'https://www.youtube.com/v/7CqlTU9aOR4', +} + +Joins and nested tables. + +export const Page = ({ children }) => + +export default Page From 0990c4772c03a32e1725f59bd7cd840fa2efe3ec Mon Sep 17 00:00:00 2001 From: Ollie Halliday <22655069+Hallidayo@users.noreply.github.com> Date: Thu, 23 Mar 2023 16:50:29 +0000 Subject: [PATCH 16/47] changed Select to Listbox --- .../Organization/TeamSettings/InviteMemberButton.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx b/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx index 8dd437ebe3d..b1d77f2e151 100644 --- a/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx +++ b/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx @@ -2,7 +2,7 @@ import { isNil } from 'lodash' import { useEffect, useState } from 'react' import { object, string } from 'yup' import * as Tooltip from '@radix-ui/react-tooltip' -import { Button, Form, IconMail, Input, Modal, Select } from 'ui' +import { Button, Form, IconMail, Input, Listbox, Modal } from 'ui' import { Member, Role } from 'types' import { checkPermissions, useParams, useStore } from 'hooks' @@ -153,7 +153,7 @@ const InviteMemberButton = ({
{roles && ( - + )}
+ Date: Thu, 23 Mar 2023 16:51:46 +0000 Subject: [PATCH 17/47] ran prettier --- .../interfaces/Organization/TeamSettings/InviteMemberButton.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx b/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx index b1d77f2e151..4b3b8648b40 100644 --- a/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx +++ b/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx @@ -172,7 +172,6 @@ const InviteMemberButton = ({ )}
- Date: Thu, 23 Mar 2023 19:13:52 -0500 Subject: [PATCH 18/47] FIX: add await to async function in code snippet The `getSession` function defined in the hook is asynchronous and the server load of the layout was not awaiting it, therefore, it was sending a promise instead of the session. Added `await`. --- apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx b/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx index f4d71380697..99449d191e0 100644 --- a/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx +++ b/apps/docs/pages/guides/auth/auth-helpers/sveltekit.mdx @@ -86,7 +86,7 @@ import type { LayoutServerLoad } from './$types'; export const load: LayoutServerLoad = async ({ locals: { getSession } }) => { return { - session: getSession() + session: await getSession() }; }; ``` From a07e53387fd8f6b3ce6381d3702baceac60a0ddf Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Fri, 24 Mar 2023 13:51:45 +0800 Subject: [PATCH 19/47] More refactoring of the Serveless API section --- .../NavigationMenu.constants.ts | 19 ++- apps/docs/pages/guides/api.mdx | 87 +++-------- .../docs/pages/guides/api/creating-routes.mdx | 13 ++ .../api/{rest => }/joins-and-nesting.mdx | 2 +- apps/docs/pages/guides/api/quickstart.mdx | 136 ++++++++++++++++++ .../pages/guides/api/securing-your-api.mdx | 124 ++++++++++++++++ 6 files changed, 306 insertions(+), 75 deletions(-) create mode 100644 apps/docs/pages/guides/api/creating-routes.mdx rename apps/docs/pages/guides/api/{rest => }/joins-and-nesting.mdx (87%) create mode 100644 apps/docs/pages/guides/api/quickstart.mdx create mode 100644 apps/docs/pages/guides/api/securing-your-api.mdx diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 441c7ca2aab..0198df856e1 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -556,15 +556,28 @@ export const api = { url: '/guides/api', items: [ { name: 'Overview', url: '/guides/api', items: [] }, + { name: 'Quickstart', url: '/guides/api/quickstart', items: [] }, + { + name: 'Guides', + url: '/guides/api', + items: [ + { name: 'Creating API routes', url: '/guides/api/creating-routes', items: [] }, + { name: 'Securing your API', url: '/guides/api/securing-your-api', items: [] }, + { + name: 'Querying joins and nested tables', + url: '/guides/api/joins-and-nesting', + items: [], + }, + ], + }, { name: 'REST', url: undefined, items: [ + { name: 'Auto-generated Docs', url: '/guides/api/rest/auto-generated-docs', items: [] }, { name: 'Client Libraries', url: '/guides/api/rest/client-libs', items: [] }, { name: 'Generating Types', url: '/guides/api/rest/generating-types', items: [] }, - { name: 'Auto-generated Docs', url: '/guides/api/rest/auto-generated-docs', items: [] }, - { name: 'Joins and Nested Tables', url: '/guides/api/rest/joins-and-nesting', items: [] }, - { name: 'Caching', url: '/guides/api/rest/caching', items: [] }, + { name: 'Caching responses', url: '/guides/api/rest/caching', items: [] }, ], }, { diff --git a/apps/docs/pages/guides/api.mdx b/apps/docs/pages/guides/api.mdx index 04e5ecb97db..e72f70211db 100644 --- a/apps/docs/pages/guides/api.mdx +++ b/apps/docs/pages/guides/api.mdx @@ -10,11 +10,15 @@ export const meta = { Supabase auto-generates three types of API directly from your database schema. -- REST - interact with your database through a restful interface. -- Realtime - listen to database changes. -- GraphQL - [in beta](https://supabase.com/blog/pg-graphql). +- REST - connect to your database through a restful interface, directly from the browser. +- Realtime - for listening to database changes. +- GraphQL - a fast and auto-generate GraphQL API. -The APIs are: +All the APIs are auto-generated from your database and are designed to get you building as fast as possible, without writing a single line of code. + +You can use them directly from the browser (two-tier architecture), or as a complement to your own API server (three-tier architecture). + +## Features - **Instant and auto-generated.**
As you update your database the changes are immediately accessible through your API. - **Self documenting.**
Supabase generates documentation in the Dashboard which updates as you make database changes. @@ -97,13 +101,12 @@ This creates a corresponding route `todos` which can accept `GET`, `POST`, `PATC ```sql --- Create a table called "todos" with a column to store tasks. - -create table todos ( - id bigint generated by default as identity primary key, - task text check (char_length(task) > 3) -); - + -- Create a table called "todos" with a column to store tasks. +create table + todos ( + id bigint generated by default as identity primary key, + task text check (char_length(task) > 3) + ); ``` @@ -314,7 +317,8 @@ By default Realtime is disabled on your database. Let's turn on Realtime for the ```sql -alter publication supabase_realtime add table todos; +alter + publication supabase_realtime add table todos; ``` @@ -340,65 +344,6 @@ Use [subscribe()](/docs/reference/javascript/subscribe) to listen to database ch The Realtime API works through PostgreSQL's replication functionality. Postgres sends database changes to a [publication](/docs/guides/database/replication#publications) called `supabase_realtime`, and by managing this publication you can control which data is broadcast. -## API Security - -### Securing your Routes - -Your API is designed to work with Postgres Row Level Security (RLS). If you use Supabase [Auth](/docs/guides/auth), you can restrict data based on the logged-in user. -To control access to your data, you can use [Policies](/docs/guides/auth#policies). -When you create a table in Postgres, Row Level Security is disabled by default. To enable RLS: - - - - -1. Go to the [Authentication](https://app.supabase.com/project/_/auth/users) page in the Dashboard. -2. Click on **Policies** in the sidebar. -3. Select **Enable RLS** to enable Row Level Security. - - - - -```sql -alter table todos enable row level security; -``` - - - - -### The `service_role` key - -Never expose the `service_role` key in a browser or anywhere where a user can see it. This Key is designed to bypass Row Level Security - so it should only be used on a private server. - -A common use case for the `service_role` key is to run data analytics jobs on the backend. To support joins on user id, it is often useful to grant the service role read access to `auth.users` table. - -```sql -grant select on table auth.users to service_role; -``` - -We have [partnered with GitHub](https://github.blog/changelog/2022-03-28-supabase-is-now-a-github-secret-scanning-partner/) to scan for Supabase `service_role` keys pushed to public repositories. -If they detect any keys with service_role privileges being pushed to GitHub, they will forward the API key to us, so that we can automatically revoke the detected secrets and notify you, protecting your data against malicious actors. - -### Safeguards towards accidental deletes and updates - -For all projects, by default, the Postgres extension [safeupdate](https://github.com/eradman/pg-safeupdate) is enabled for all queries coming from the API. -This ensures that any `delete()` or `update()` would fail if there are no accompanying filters provided. -To confirm that safeupdate is enabled for queries going through the API of your project, the following query could be run: - -```sql -select usename, useconfig from pg_shadow where usename = 'authenticator'; -``` - -The expected value for `useconfig` should be: - -```sql -['session_preload_libraries=supautils, safeupdate'] -``` - export const Page = ({ children }) => export default Page diff --git a/apps/docs/pages/guides/api/creating-routes.mdx b/apps/docs/pages/guides/api/creating-routes.mdx new file mode 100644 index 00000000000..97ba52d0e1e --- /dev/null +++ b/apps/docs/pages/guides/api/creating-routes.mdx @@ -0,0 +1,13 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'creating-routes', + title: 'Creating API Routes', + description: 'A description.', +} + +Creating routes + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/api/rest/joins-and-nesting.mdx b/apps/docs/pages/guides/api/joins-and-nesting.mdx similarity index 87% rename from apps/docs/pages/guides/api/rest/joins-and-nesting.mdx rename to apps/docs/pages/guides/api/joins-and-nesting.mdx index f0a3abc4c98..a7f460c6691 100644 --- a/apps/docs/pages/guides/api/rest/joins-and-nesting.mdx +++ b/apps/docs/pages/guides/api/joins-and-nesting.mdx @@ -2,7 +2,7 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'joins-and-nested-tables', - title: 'Joins and Nested tables', + title: 'Querying Joins and Nested tables', description: 'A description.', video: 'https://www.youtube.com/v/7CqlTU9aOR4', } diff --git a/apps/docs/pages/guides/api/quickstart.mdx b/apps/docs/pages/guides/api/quickstart.mdx new file mode 100644 index 00000000000..bb0ac03dcaf --- /dev/null +++ b/apps/docs/pages/guides/api/quickstart.mdx @@ -0,0 +1,136 @@ +import Layout from '~/layouts/DefaultGuideLayout' +import StepHikeCompact from '~/components/StepHikeCompact' + +export const meta = { + title: 'Query your database in less than 2 minutes.', + subtitle: 'Create your first API route by creating a table called `todos` to store tasks.', + breadcrumb: 'API Quickstart', +} + +Let's create our first API route by creating a table called `todos` to store tasks. + +This creates a corresponding route `todos` which can accept `GET`, `POST`, `PATCH`, & `DELETE` requests. + + + + + + + [Create a new project](https://app.supabase.com) in the Supabase Dashboard. + + After your project is ready, create a table in your Supabase database. You can do this with either the Table interface or the [SQL Editor](https://app.supabase.com/project/_/sql). + + + + + + + + + ```sql + -- Create a table called "todos" + -- with a column to store tasks. + create table todos ( + id serial primary key, + task text + ); + ``` + + + + + + + + + + + + + + + + + + Let's turn on Row Level Security for this table and allow anonymous access. + + + + + + ```sql + -- Turn on security + alter table "todos" + enable row level security; + + -- Allow anonymous access + create policy "Allow anonymous access" + on todos + to anon + for select + using (true); + ``` + + + + + + + + + Now we can add some data to our table which we can access through our API. + + + + + + ```sql + insert into todos (task) + values + ('Create tables'), + ('Enable security'), + ('Add data'), + ('Fetch data from the API'); + ``` + + + + + + + + + Find your API URL and Keys in your Dashboard [API Settings](https://app.supabase.com/project/_/settings/api). You can now query your "todos" table by appending `/rest/v1/todos` to the API URL. + + Copy this block of code, substitute `` and ``, then run it from a terminal. + + + + + ```bash Terminal + curl 'https://.supabase.co/rest/v1/todos' \ + -H "apikey: " \ + -H "Authorization: Bearer " + ``` + + + + + + + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/api/securing-your-api.mdx b/apps/docs/pages/guides/api/securing-your-api.mdx new file mode 100644 index 00000000000..6942d6d390d --- /dev/null +++ b/apps/docs/pages/guides/api/securing-your-api.mdx @@ -0,0 +1,124 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'securing-your-api', + title: 'Securing your API', + description: 'Securing your Serverless API with Postgres Row Level Security.', +} + +The Serverless APIs are designed to work with Postgres Row Level Security (RLS). If you use Supabase [Auth](/docs/guides/auth), you can restrict data based on the logged-in user. +To control access to your data, you can use [Policies](/docs/guides/auth#policies). + +## Enabling Row Level Security + +When you create a table in Postgres, Row Level Security is disabled by default. To enable RLS: + + + + +1. Go to the [Authentication](https://app.supabase.com/project/_/auth/users) page in the Dashboard. +2. Click on **Policies** in the sidebar. +3. Select **Enable RLS** to enable Row Level Security. + + + + +```sql +alter table + todos enable row level security; +``` + + + + +With RLS enabled, you can create Policies that allow or disallow users to access and update data. We provide a detailed guide for creating Row Level Security Policies in our [Authorization documentation](/docs/guides/auth/row-level-security). + +## API Keys + +Supabase provides two default keys when you create a project: an `anon` key, and a `service_role` key. You can find both keys in the [API Settings](https://app.supabase.com/project/_/settings/api). + +These keys both map to Postgres roles - you can find an `anon` user and a `service_role` user in the [Roles](http://app.supabase.com/project/_/database/roles) section of the dashboard. The keys are both long-lived JWTs. If you decode these keys, you will see that they contain the "role", an "issued date", and an "expiry date" in the future. + +```json +{ + "role": "anon", + "iat": 1625137684, + "exp": 1940713684 +} +``` + +### The `anon` key + +The `anon` key has very few privileges. You can use it in your [RLS policies](/docs/guides/auth/row-level-security) for "anonymous" access. For example, this policy will allow access to the `profiles` table: + +```sql +create policy "Allow anonymous access" on profiles to anon for +select + using (true); +``` + +And similarity for disallowing access: + +```sql +create policy "Disallow anonymous access" on profiles to anon for +select + using (false); +``` + +If you are using [Supabase Auth](/docs/guides/auth/overview), then the `anon` role will automatically update to `authenticated` once a user is logged in: + +```sql +create policy "Allow access to authenticated users" on profiles to authenticated for +select + using (true); +``` + +### The `service_role` key + +The "service_role" is a predefined Postgres role with elevated privileges, designed to perform various administrative and service-related tasks. It can bypass Row Level Security, so it should only be used on a private server. + + + Never expose the `service_role` key in a browser or anywhere where a user can see it. + + +A common use case for the `service_role` key is running data analytics jobs on the backend. To support joins on user id, it is often useful to grant the service role read access to `auth.users` table. + +```sql +grant +select + on table auth.users to service_role; +``` + +We have [partnered with GitHub](https://github.blog/changelog/2022-03-28-supabase-is-now-a-github-secret-scanning-partner/) to scan for Supabase `service_role` keys pushed to public repositories. +If they detect any keys with service_role privileges being pushed to GitHub, they will forward the API key to us, so that we can automatically revoke the detected secrets and notify you, protecting your data against malicious actors. + +## Safeguards towards accidental deletes and updates + +By default, all projects have the [safeupdate](https://github.com/eradman/pg-safeupdate) Postgres extension enabled for API queries. +This ensures that `delete()` and `update()` requests will fail if there are no filters provided. +To confirm that safeupdate is enabled for API queries, run the following query: + +```sql +select + usename, + useconfig +from + pg_shadow +where + usename = 'authenticator'; +``` + +The expected value for `useconfig` should be: + +```sql +['session_preload_libraries=supautils, safeupdate'] +``` + +export const Page = ({ children }) => + +export default Page From cc664aa3b59eba9bd85725b046aeb394c83c5a57 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Fri, 24 Mar 2023 15:47:43 +0800 Subject: [PATCH 20/47] More cleanup --- .../NavigationMenu.constants.ts | 5 +- apps/docs/pages/guides/api.mdx | 282 ------------------ apps/docs/pages/guides/api/api-keys.mdx | 70 +++++ .../docs/pages/guides/api/creating-routes.mdx | 243 ++++++++++++++- .../pages/guides/api/graphql/graphiql.mdx | 10 +- .../pages/guides/api/joins-and-nesting.mdx | 198 +++++++++++- apps/docs/pages/guides/api/quickstart.mdx | 106 ++++++- .../guides/api/rest/auto-generated-docs.mdx | 18 +- .../pages/guides/api/securing-your-api.mdx | 61 +--- 9 files changed, 637 insertions(+), 356 deletions(-) create mode 100644 apps/docs/pages/guides/api/api-keys.mdx diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 0198df856e1..7824f3549fe 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -562,6 +562,7 @@ export const api = { url: '/guides/api', items: [ { name: 'Creating API routes', url: '/guides/api/creating-routes', items: [] }, + { name: 'How API Keys work', url: '/guides/api/api-keys', items: [] }, { name: 'Securing your API', url: '/guides/api/securing-your-api', items: [] }, { name: 'Querying joins and nested tables', @@ -571,7 +572,7 @@ export const api = { ], }, { - name: 'REST', + name: 'REST & REALTIME', url: undefined, items: [ { name: 'Auto-generated Docs', url: '/guides/api/rest/auto-generated-docs', items: [] }, @@ -583,7 +584,7 @@ export const api = { { name: 'GRAPHQL', url: undefined, - items: [{ name: 'GraphiQL', url: '/guides/api/graphql/graphiql', items: [] }], + items: [{ name: 'GraphiQL Documentation', url: '/guides/api/graphql/graphiql', items: [] }], }, ], } diff --git a/apps/docs/pages/guides/api.mdx b/apps/docs/pages/guides/api.mdx index e72f70211db..8cd1bccf46b 100644 --- a/apps/docs/pages/guides/api.mdx +++ b/apps/docs/pages/guides/api.mdx @@ -62,288 +62,6 @@ Supabase provides a Realtime API using [Realtime](https://github.com/supabase/re Realtime leverages PostgreSQL's built-in logical replication. You can manage your Realtime API simply by managing Postgres publications. Go to your project's [Replication section](https://app.supabase.com/project/_/database/replication) to get started. -## Getting started - -All APIs are auto-created from Database tables. After you have added tables or functions to your database, you can use the APIs provided. - -### Creating API Routes - -API routes are automatically created when you create Postgres Tables, Views, or Functions. - -Let's create our first -API route by creating a table called `todos` to store tasks. -This creates a corresponding route `todos` which can accept `GET`, `POST`, `PATCH`, & `DELETE` requests. - - - - -1. Go to the [Table editor](https://app.supabase.com/project/_/editor) page in the Dashboard. -1. Click **New Table** and create a table with the name `todos`. -1. Click **Save**. -1. Click **New Column** and create a column with the name `task` and type `text`. -1. Click **Save**. - - - - - - -```sql - -- Create a table called "todos" with a column to store tasks. -create table - todos ( - id bigint generated by default as identity primary key, - task text check (char_length(task) > 3) - ); -``` - - - - -### API URL and Keys - -Every Supabase project has a unique API URL. Your API is secured behind an API gateway which requires an API Key for every request. - -1. Go to the [Settings](https://app.supabase.com/project/_/settings/general) page in the Dashboard. -2. Click **API** in the sidebar. -3. Find your API `URL`, `anon`, and `service_role` keys on this page. - - - -The REST API and the GraphQL API are both accessible through this URL: - -- REST: `https://.supabase.co/rest/v1` -- GraphQL: `https://.supabase.co/graphql/v1` - -Both of these routes require the `anon` key to be passed through an `apikey` header. - -#### API Keys - -You are provided with two keys: - -- an `anon` key, which is safe to be used in a browser context. -- a `service_role` key, which should only be used on a server. This key can bypass Row Level Security. NEVER use this key in a browser. - -### Accessing the docs in the Dashboard - -#### REST API [#rest-api-dashboard-docs] - -Supabase generates documentation in the [Dashboard](https://app.supabase.com) which updates as you make database changes. -Let's view the documentation for a `countries` table which we created in our database. - -1. Go to the [API](https://app.supabase.com/project/_/api) page in the Dashboard. -2. Find the `countries` table under **Tables and Views** in the sidebar. -3. Switch between the JavaScript and the cURL docs using the tabs. - - - -#### GraphQL - -The GraphQL Endpoint that we provide (`https://.supabase.co/graphql/v1`) is compatible with any GraphiQL implementation that can pass an `apikey` header. -Some suggested applications: - -- [paw.cloud](https://paw.cloud) -- [insomnia.rest](https://insomnia.rest) -- [postman.com/graphql](https://www.postman.com/graphql/) -- Self-hosted GraphiQL: GraphiQL can be served through a simple HTML file. See [this discussion](https://github.com/supabase/supabase/discussions/6144) for more details. - -## Using the API - -### REST API - -You can interact with your API directly via HTTP requests, or you can use the client libraries which we provide. - -Let's see how to make a request to the `todos` table which we created in the first step, -using the API URL (`SUPABASE_URL`) and Key (`SUPABASE_ANON_KEY`) we provided: - - - - -```javascript -// Initialize the JS client -import { createClient } from '@supabase/supabase-js' -const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY) - -// Make a request -const { data: todos, error } = await supabase.from('todos').select('*') -``` - - - - -```bash -# Append /rest/v1/ to your URL, and then use the table name as the route -curl '/rest/v1/todos' \ --H "apikey: " \ --H "Authorization: Bearer " -``` - - - - -JS Reference: [select()](/docs/reference/javascript/select), -[insert()](/docs/reference/javascript/insert), -[update()](/docs/reference/javascript/update), -[upsert()](/docs/reference/javascript/upsert), -[delete()](/docs/reference/javascript/delete), -[rpc()](/docs/reference/javascript/rpc) (call Postgres functions). - -### GraphQL API - -You can use any GraphQL client with the Supabase GraphQL API. For our GraphQL example we will use [urql](https://formidable.com/open-source/urql/docs/). - - - - -```javascript -import { createClient, useQuery } from 'urql' - -// Prepare API key and Authorization header -const headers = { - apikey: , - authorization: `Bearer ${}`, -} - -// Create GraphQL client -// See: https://formidable.com/open-source/urql/docs/basics/react-preact/#setting-up-the-client -const client = createClient({ - url: '/graphql/v1', - fetchOptions: function createFetchOptions() { - return { headers } - }, -}) - -// Prepare our GraphQL query -const TodosQuery = ` - query { - todosCollection { - edges { - node { - id - title - } - } - } - } -` - -// Query for the data (React) -const [result, reexecuteQuery] = useQuery({ - query: TodosQuery, -}) - -// Read the result -const { data, fetching, error } = result -``` - - - - -```bash -# Append /graphql/v1/ to your URL, and then use the table name as the route -curl --request POST '/graphql/v1' \ --H 'apikey: ' \ --H 'Authorization: Bearer ' \ --H 'Content-Type: application/json' \ --d '{ "query":"{ todos(first: 3) { edges { node { id } } } }" }' -``` - - - - -### Realtime API - -By default Realtime is disabled on your database. Let's turn on Realtime for the `todos` table. - - - - -1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. -2. Click on **Replication** in the sidebar. -3. Control which database events are sent by toggling **Insert**, **Update**, and **Delete**. -4. Control which tables broadcast changes by selecting **Source** and toggling each table. - - - - - - -```sql -alter - publication supabase_realtime add table todos; -``` - - - - -From the client, we can listen to any new data that is inserted into the `todos` table: - -```javascript -// Initialize the JS client -import { createClient } from '@supabase/supabase-js' -const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY) - -// Create a function to handle inserts -const handleInserts = (payload) => { - console.log('Change received!', payload) -} - -// Listen to inserts -const { data: todos, error } = await supabase.from('todos').on('INSERT', handleInserts).subscribe() -``` - -Use [subscribe()](/docs/reference/javascript/subscribe) to listen to database changes. -The Realtime API works through PostgreSQL's replication functionality. Postgres sends database changes to a [publication](/docs/guides/database/replication#publications) -called `supabase_realtime`, and by managing this publication you can control which data is broadcast. - export const Page = ({ children }) => export default Page diff --git a/apps/docs/pages/guides/api/api-keys.mdx b/apps/docs/pages/guides/api/api-keys.mdx new file mode 100644 index 00000000000..3f9869586f5 --- /dev/null +++ b/apps/docs/pages/guides/api/api-keys.mdx @@ -0,0 +1,70 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'api-keys', + title: 'Understanding API Keys', + description: 'Securing your Serverless API with Postgres Row Level Security.', +} + +Supabase provides two default keys when you create a project: an `anon` key, and a `service_role` key. You can find both keys in the [API Settings](https://app.supabase.com/project/_/settings/api). + +The Serverless APIs are designed to work with Postgres Row Level Security (RLS). These keys both map to Postgres roles. You can find an `anon` user and a `service_role` user in the [Roles](http://app.supabase.com/project/_/database/roles) section of the dashboard. + +The keys are both long-lived JWTs. If you decode these keys, you will see that they contain the "role", an "issued date", and an "expiry date" ~10 years in the future. + +```json +{ + "role": "anon", + "iat": 1625137684, + "exp": 1940713684 +} +``` + +## The `anon` key + +The `anon` key has very few privileges. You can use it in your [RLS policies](/docs/guides/auth/row-level-security) for "anonymous" access. For example, this policy will allow access to the `profiles` table: + +```sql +create policy "Allow anonymous access" on profiles to anon for +select + using (true); +``` + +And similarity for disallowing access: + +```sql +create policy "Disallow anonymous access" on profiles to anon for +select + using (false); +``` + +If you are using [Supabase Auth](/docs/guides/auth/overview), then the `anon` role will automatically update to `authenticated` once a user is logged in: + +```sql +create policy "Allow access to authenticated users" on profiles to authenticated for +select + using (true); +``` + +## The `service_role` key + +The "service_role" is a predefined Postgres role with elevated privileges, designed to perform various administrative and service-related tasks. It can bypass Row Level Security, so it should only be used on a private server. + + + Never expose the `service_role` key in a browser or anywhere where a user can see it. + + +A common use case for the `service_role` key is running data analytics jobs on the backend. To support joins on user id, it is often useful to grant the service role read access to `auth.users` table. + +```sql +grant +select + on table auth.users to service_role; +``` + +We have [partnered with GitHub](https://github.blog/changelog/2022-03-28-supabase-is-now-a-github-secret-scanning-partner/) to scan for Supabase `service_role` keys pushed to public repositories. +If they detect any keys with service_role privileges being pushed to GitHub, they will forward the API key to us, so that we can automatically revoke the detected secrets and notify you, protecting your data against malicious actors. + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/api/creating-routes.mdx b/apps/docs/pages/guides/api/creating-routes.mdx index 97ba52d0e1e..c48442f12d1 100644 --- a/apps/docs/pages/guides/api/creating-routes.mdx +++ b/apps/docs/pages/guides/api/creating-routes.mdx @@ -3,10 +3,249 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'creating-routes', title: 'Creating API Routes', - description: 'A description.', + description: + 'API routes are automatically created when you create Postgres Tables, Views, or Functions.', } -Creating routes +API routes are automatically created when you create Postgres Tables, Views, or Functions. + +## Create a table + +Let's create our first API route by creating a table called `todos` to store tasks. +This creates a corresponding route `todos` which can accept `GET`, `POST`, `PATCH`, & `DELETE` requests. + + + + +1. Go to the [Table editor](https://app.supabase.com/project/_/editor) page in the Dashboard. +1. Click **New Table** and create a table with the name `todos`. +1. Click **Save**. +1. Click **New Column** and create a column with the name `task` and type `text`. +1. Click **Save**. + + + + + + +```sql + -- Create a table called "todos" with a column to store tasks. +create table + todos ( + id bigint generated by default as identity primary key, + task text check (char_length(task) > 3) + ); +``` + + + + +## API URL and Keys + +Every Supabase project has a unique API URL. Your API is secured behind an API gateway which requires an API Key for every request. + +1. Go to the [Settings](https://app.supabase.com/project/_/settings/general) page in the Dashboard. +2. Click **API** in the sidebar. +3. Find your API `URL`, `anon`, and `service_role` keys on this page. + + + +The REST API and the GraphQL API are both accessible through this URL: + +- REST: `https://.supabase.co/rest/v1` +- GraphQL: `https://.supabase.co/graphql/v1` + +Both of these routes require the `anon` key to be passed through an `apikey` header. + +## Using the API + +### REST API + +You can interact with your API directly via HTTP requests, or you can use the client libraries which we provide. + +Let's see how to make a request to the `todos` table which we created in the first step, +using the API URL (`SUPABASE_URL`) and Key (`SUPABASE_ANON_KEY`) we provided: + + + + +```javascript +// Initialize the JS client +import { createClient } from '@supabase/supabase-js' +const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY) + +// Make a request +const { data: todos, error } = await supabase.from('todos').select('*') +``` + + + + +```bash +# Append /rest/v1/ to your URL, and then use the table name as the route +curl '/rest/v1/todos' \ +-H "apikey: " \ +-H "Authorization: Bearer " +``` + + + + +JS Reference: [select()](/docs/reference/javascript/select), +[insert()](/docs/reference/javascript/insert), +[update()](/docs/reference/javascript/update), +[upsert()](/docs/reference/javascript/upsert), +[delete()](/docs/reference/javascript/delete), +[rpc()](/docs/reference/javascript/rpc) (call Postgres functions). + +### GraphQL API + +You can use any GraphQL client with the Supabase GraphQL API. For our GraphQL example we will use [urql](https://formidable.com/open-source/urql/docs/). + + + + +```javascript +import { createClient, useQuery } from 'urql' + +// Prepare API key and Authorization header +const headers = { + apikey: , + authorization: `Bearer ${}`, +} + +// Create GraphQL client +// See: https://formidable.com/open-source/urql/docs/basics/react-preact/#setting-up-the-client +const client = createClient({ + url: '/graphql/v1', + fetchOptions: function createFetchOptions() { + return { headers } + }, +}) + +// Prepare our GraphQL query +const TodosQuery = ` + query { + todosCollection { + edges { + node { + id + title + } + } + } + } +` + +// Query for the data (React) +const [result, reexecuteQuery] = useQuery({ + query: TodosQuery, +}) + +// Read the result +const { data, fetching, error } = result +``` + + + + +```bash +# Append /graphql/v1/ to your URL, and then use the table name as the route +curl --request POST '/graphql/v1' \ +-H 'apikey: ' \ +-H 'Authorization: Bearer ' \ +-H 'Content-Type: application/json' \ +-d '{ "query":"{ todos(first: 3) { edges { node { id } } } }" }' +``` + + + + +### Realtime API + +By default Realtime is disabled on your database. Let's turn on Realtime for the `todos` table. + + + + +1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +2. Click on **Replication** in the sidebar. +3. Control which database events are sent by toggling **Insert**, **Update**, and **Delete**. +4. Control which tables broadcast changes by selecting **Source** and toggling each table. + + + + + + +```sql +alter + publication supabase_realtime add table todos; +``` + + + + +From the client, we can listen to any new data that is inserted into the `todos` table: + +```javascript +// Initialize the JS client +import { createClient } from '@supabase/supabase-js' +const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY) + +// Create a function to handle inserts +const handleInserts = (payload) => { + console.log('Change received!', payload) +} + +// Listen to inserts +const { data: todos, error } = await supabase.from('todos').on('INSERT', handleInserts).subscribe() +``` + +Use [subscribe()](/docs/reference/javascript/subscribe) to listen to database changes. +The Realtime API works through PostgreSQL's replication functionality. Postgres sends database changes to a [publication](/docs/guides/database/replication#publications) +called `supabase_realtime`, and by managing this publication you can control which data is broadcast. export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/api/graphql/graphiql.mdx b/apps/docs/pages/guides/api/graphql/graphiql.mdx index 9887e72278f..a3ce41073fd 100644 --- a/apps/docs/pages/guides/api/graphql/graphiql.mdx +++ b/apps/docs/pages/guides/api/graphql/graphiql.mdx @@ -7,7 +7,15 @@ export const meta = { video: 'https://www.youtube.com/v/7CqlTU9aOR4', } -GraphiQL +Every Supabase project has a GraphQL Endpoint: `https://.supabase.co/graphql/v1`. + +This endpoint is compatible with any GraphiQL implementation that can pass an `apikey` header. +Some suggested applications: + +- [paw.cloud](https://paw.cloud) +- [insomnia.rest](https://insomnia.rest) +- [postman.com/graphql](https://www.postman.com/graphql/) +- Self-hosted GraphiQL: GraphiQL can be served through a simple HTML file. See [this discussion](https://github.com/supabase/supabase/discussions/6144) for more details. export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/api/joins-and-nesting.mdx b/apps/docs/pages/guides/api/joins-and-nesting.mdx index a7f460c6691..a5fe6f41231 100644 --- a/apps/docs/pages/guides/api/joins-and-nesting.mdx +++ b/apps/docs/pages/guides/api/joins-and-nesting.mdx @@ -3,11 +3,203 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'joins-and-nested-tables', title: 'Querying Joins and Nested tables', - description: 'A description.', - video: 'https://www.youtube.com/v/7CqlTU9aOR4', + description: 'The Serverless APIs automatically detect relationships between Postgres tables.', } -Joins and nested tables. +The Serverless APIs automatically detect relationships between Postgres tables. Since Postgres is a relational database, this is a very common scenario. + +## One-to-many joins + +Let's use an example database that stores `countries` and `cities`: + + + + +**Countries** + +| `id` | `name` | +| ---- | -------------- | +| 1 | United Kingdom | +| 2 | United States | + +**Cities** + +| `id` | `name` | `country_id` | +| ---- | ----------- | ------------ | +| 1 | London | 1 | +| 2 | Manchester | 1 | +| 3 | Los Angeles | 2 | +| 4 | New York | 2 | + + + + +```sql +create table + countries ("id" serial primary key, "name" text); + +insert into + countries (id, name) +values + (1, 'United Kingdom'), + (2, 'United States'); + +create table + cities ( + "id" serial primary key, + "name" text, + "country_id" int references "countries" + ); + +insert into + cities (name, country_id) +values + ('London', 1), + ('Manchester', 1), + ('Los Angeles', 2), + ('New York', 2); +``` + + + + +The APIs will automatically detect relationships based on the foreign keys: + + + + +```js +const { data, error } = await supabase.from('countries').select(` + id, + name, + cities ( id, name ) +`) +``` + + + + +```dart +final data = await supabase.from('todos').select('id, name, cities(id, name)'); +``` + + + + +```javascript +const Query = ` + query { + countriesCollection { + edges { + node { + id + name + cities { + id, + name + } + } + } + } + } +` +``` + + + + +```bash +GET https://[REF].supabase.co/rest/v1/countries?select=id,name,cities(id,name) +``` + + + + +## Many-to-many joins + +The Serverless APIs will detect many-to-many joins. For example, if you have a database which stored teams of users (where each user could below to many teams): + +```sql +create table + users ("id" serial primary key, "name" text); + +create table + teams ("id" serial primary key, "team_name" text); + +create table + members ( + "id" serial primary key, + "user_id" int references users, + "team_id" int references teams + ); +``` + +In these cases you don't need to explicity define the joining table (members). If we wanted to fetch all the teams and the members in each team: + + + + +```js +const { data, error } = await supabase.from('teams').select(` + id, + team_name, + users ( id, name ) +`) +``` + + + + +```dart +final data = await supabase.from('teams').select('id, team_name, users(id, name)'); +``` + + + + +```javascript +const Query = ` + query { + teamsCollection { + edges { + node { + id + team_name + users { + id, + name + } + } + } + } + } +` +``` + + + + +```bash +GET https://[REF].supabase.co/rest/v1/teams?select=id,team_name,users(id,name) +``` + + + export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/api/quickstart.mdx b/apps/docs/pages/guides/api/quickstart.mdx index bb0ac03dcaf..7d645a8b5f7 100644 --- a/apps/docs/pages/guides/api/quickstart.mdx +++ b/apps/docs/pages/guides/api/quickstart.mdx @@ -2,14 +2,14 @@ import Layout from '~/layouts/DefaultGuideLayout' import StepHikeCompact from '~/components/StepHikeCompact' export const meta = { - title: 'Query your database in less than 2 minutes.', + title: 'Build an API route in less than 2 minutes.', subtitle: 'Create your first API route by creating a table called `todos` to store tasks.', breadcrumb: 'API Quickstart', } -Let's create our first API route by creating a table called `todos` to store tasks. +Let's create our first REST route which we can query using `cURL` or the browser. -This creates a corresponding route `todos` which can accept `GET`, `POST`, `PATCH`, & `DELETE` requests. +We'll create a database table called `todos` for storing tasks. This creates a corresponding API route `/rest/v1/todos` which can accept `GET`, `POST`, `PATCH`, & `DELETE` requests. @@ -131,6 +131,106 @@ This creates a corresponding route `todos` which can accept `GET`, `POST`, `PATC +## Bonus + +There are several options for accessing your data: + +### Browser + +You can query the route in your browser, by appending the `anon` key as a query parameter: + +`https://.supabase.co/rest/v1/users?apikey=` + +### Client libraries + +We provide a numerous [Client Libraries](https://github.com/supabase/supabase#client-libraries). + + + + +```js +const { data, error } = await supabase.from('todos').select() +``` + + + + +```dart +final data = await supabase.from('todos').select('*'); +``` + + + + +```python +response = supabase.table('todos').select("*").execute() +``` + + + + +### GraphQL + +Every table can be accessed through the GraphQL API by switching `/rest/v1` with `/graphql/v1`. + + + + +```javascript +import { createClient, useQuery } from 'urql' + +const URL = '/graphql/v1' +const ANON_KEY = '' + +// Prepare API key and Authorization header +const headers = { + apikey: `${ANON_KEY}`, + authorization: `Bearer ${ANON_KEY}`, +} + +const client = createClient({ + url: URL, + fetchOptions: function createFetchOptions() { + return { headers } + }, +}) + +// Prepare our GraphQL query +const TodosQuery = ` + query { + todosCollection { + edges { + node { + id + task + } + } + } + } +` + +// Query for the data (React) +const [result, reexecuteQuery] = useQuery({ + query: TodosQuery, +}) + +// Read the result +const { data, fetching, error } = result +``` + + + + export const Page = ({ children }) => export default Page diff --git a/apps/docs/pages/guides/api/rest/auto-generated-docs.mdx b/apps/docs/pages/guides/api/rest/auto-generated-docs.mdx index bde5bb7bb21..82709aa7a1a 100644 --- a/apps/docs/pages/guides/api/rest/auto-generated-docs.mdx +++ b/apps/docs/pages/guides/api/rest/auto-generated-docs.mdx @@ -3,11 +3,23 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'auto-docs', title: 'Auto-generated documentation', - description: 'Suapbase provides several client libraries for the REST interface.', - video: 'https://www.youtube.com/v/7CqlTU9aOR4', + description: 'Supabase provides documentation that updates automatically.', } -A doc on the Client Libraries, how they are structured, and where to find the docs. +Supabase generates documentation in the [Dashboard](https://app.supabase.com) which updates as you make database changes. + +1. Go to the [API](https://app.supabase.com/project/_/api) page in the Dashboard. +2. Select any table under **Tables and Views** in the sidebar. +3. Switch between the JavaScript and the cURL docs using the tabs. + + export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/api/securing-your-api.mdx b/apps/docs/pages/guides/api/securing-your-api.mdx index 6942d6d390d..a1747d75943 100644 --- a/apps/docs/pages/guides/api/securing-your-api.mdx +++ b/apps/docs/pages/guides/api/securing-your-api.mdx @@ -6,7 +6,7 @@ export const meta = { description: 'Securing your Serverless API with Postgres Row Level Security.', } -The Serverless APIs are designed to work with Postgres Row Level Security (RLS). If you use Supabase [Auth](/docs/guides/auth), you can restrict data based on the logged-in user. +The Serverless APIs are designed to work with Postgres Row Level Security (RLS). If you use [Supabase Auth](/docs/guides/auth), you can restrict data based on the logged-in user. To control access to your data, you can use [Policies](/docs/guides/auth#policies). ## Enabling Row Level Security @@ -38,65 +38,6 @@ alter table With RLS enabled, you can create Policies that allow or disallow users to access and update data. We provide a detailed guide for creating Row Level Security Policies in our [Authorization documentation](/docs/guides/auth/row-level-security). -## API Keys - -Supabase provides two default keys when you create a project: an `anon` key, and a `service_role` key. You can find both keys in the [API Settings](https://app.supabase.com/project/_/settings/api). - -These keys both map to Postgres roles - you can find an `anon` user and a `service_role` user in the [Roles](http://app.supabase.com/project/_/database/roles) section of the dashboard. The keys are both long-lived JWTs. If you decode these keys, you will see that they contain the "role", an "issued date", and an "expiry date" in the future. - -```json -{ - "role": "anon", - "iat": 1625137684, - "exp": 1940713684 -} -``` - -### The `anon` key - -The `anon` key has very few privileges. You can use it in your [RLS policies](/docs/guides/auth/row-level-security) for "anonymous" access. For example, this policy will allow access to the `profiles` table: - -```sql -create policy "Allow anonymous access" on profiles to anon for -select - using (true); -``` - -And similarity for disallowing access: - -```sql -create policy "Disallow anonymous access" on profiles to anon for -select - using (false); -``` - -If you are using [Supabase Auth](/docs/guides/auth/overview), then the `anon` role will automatically update to `authenticated` once a user is logged in: - -```sql -create policy "Allow access to authenticated users" on profiles to authenticated for -select - using (true); -``` - -### The `service_role` key - -The "service_role" is a predefined Postgres role with elevated privileges, designed to perform various administrative and service-related tasks. It can bypass Row Level Security, so it should only be used on a private server. - - - Never expose the `service_role` key in a browser or anywhere where a user can see it. - - -A common use case for the `service_role` key is running data analytics jobs on the backend. To support joins on user id, it is often useful to grant the service role read access to `auth.users` table. - -```sql -grant -select - on table auth.users to service_role; -``` - -We have [partnered with GitHub](https://github.blog/changelog/2022-03-28-supabase-is-now-a-github-secret-scanning-partner/) to scan for Supabase `service_role` keys pushed to public repositories. -If they detect any keys with service_role privileges being pushed to GitHub, they will forward the API key to us, so that we can automatically revoke the detected secrets and notify you, protecting your data against malicious actors. - ## Safeguards towards accidental deletes and updates By default, all projects have the [safeupdate](https://github.com/eradman/pg-safeupdate) Postgres extension enabled for API queries. From 68531f3679084686993db3d169e045227ed4ae0f Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Fri, 24 Mar 2023 15:54:27 +0800 Subject: [PATCH 21/47] let everyone know how safe we are --- apps/docs/pages/guides/api/securing-your-api.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/docs/pages/guides/api/securing-your-api.mdx b/apps/docs/pages/guides/api/securing-your-api.mdx index a1747d75943..70fa2ef38ae 100644 --- a/apps/docs/pages/guides/api/securing-your-api.mdx +++ b/apps/docs/pages/guides/api/securing-your-api.mdx @@ -11,7 +11,8 @@ To control access to your data, you can use [Policies](/docs/guides/auth#policie ## Enabling Row Level Security -When you create a table in Postgres, Row Level Security is disabled by default. To enable RLS: +If you create a table through the Dashboard, RLS will be enabled by default. This is not the case, however, if you create a table or view using SQL. +To enable RLS on any table: Date: Fri, 24 Mar 2023 17:12:12 +0800 Subject: [PATCH 22/47] Cleans up, closer to finishing --- .../NavigationMenu.constants.ts | 1 - apps/docs/pages/guides/api/rest/caching.mdx | 16 ------------- .../pages/guides/api/rest/client-libs.mdx | 23 +++++++++++++++++-- 3 files changed, 21 insertions(+), 19 deletions(-) delete mode 100644 apps/docs/pages/guides/api/rest/caching.mdx diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 7824f3549fe..cdca935e92d 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -578,7 +578,6 @@ export const api = { { name: 'Auto-generated Docs', url: '/guides/api/rest/auto-generated-docs', items: [] }, { name: 'Client Libraries', url: '/guides/api/rest/client-libs', items: [] }, { name: 'Generating Types', url: '/guides/api/rest/generating-types', items: [] }, - { name: 'Caching responses', url: '/guides/api/rest/caching', items: [] }, ], }, { diff --git a/apps/docs/pages/guides/api/rest/caching.mdx b/apps/docs/pages/guides/api/rest/caching.mdx deleted file mode 100644 index fe437d7d961..00000000000 --- a/apps/docs/pages/guides/api/rest/caching.mdx +++ /dev/null @@ -1,16 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'joins-and-nested-tables', - title: 'Joins and Nested tables', - description: 'A description.', - video: 'https://www.youtube.com/v/7CqlTU9aOR4', -} - -- Supabase cache helpers: https://github.com/psteinroe/supabase-cache-helpers -- SWR -- React Query - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/api/rest/client-libs.mdx b/apps/docs/pages/guides/api/rest/client-libs.mdx index 0f5c374074d..5c018f62efe 100644 --- a/apps/docs/pages/guides/api/rest/client-libs.mdx +++ b/apps/docs/pages/guides/api/rest/client-libs.mdx @@ -3,11 +3,30 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'client-libs', title: 'Client Libraries', - description: 'Suapbase provides several client libraries for the REST interface.', + description: 'Supabase provides several client libraries for the REST and Realtime APIs.', video: 'https://www.youtube.com/v/7CqlTU9aOR4', } -A doc on the Client Libraries, how they are structured, and where to find the docs. +Supabase provides client libraries for the REST and Realtime APIs. Some libraries are officially supported, and some are contributed by the community. + +## Official Libraries + +| `Language` | `Documentation` | `Source Code` | +| --------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------- | +| Javascript/Typescript | [Docs](https://supabase.com/docs/reference/javascript/introduction) | [supabase-js](https://github.com/supabase/supabase-js) | +| Dart/Flutter | [Docs](https://supabase.com/docs/reference/dart/introduction) | [supabase-dart](https://github.com/supabase/supabase-dart) | + +## Community Libraries + +| `Language` | `Source Code` | +| ----------------------- | -------------------------------------------------------------------------------- | +| C# | [supabase-js](https://github.com/supabase-community/supabase-csharp) | +| Go | [supabase-go](https://github.com/supabase-community/supabase-go) | +| Kotlin | [supabase-kt](https://github.com/supabase-community/supabase-kt) | +| Python | [supabase-py](https://github.com/supabase-community/supabase-py) | +| Ruby | [supabase-rb](https://github.com/supabase-community/supabase-rb) | +| Swift | [supabase-swift](https://github.com/supabase-community/supabase-swift) | +| Godot Engine (GDScript) | [supabase-gdscript](https://github.com/supabase-community/godot-engine.supabase) | export const Page = ({ children }) => From ea8157900479eb59349adcc8c861973a26d5a950 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Fri, 24 Mar 2023 17:29:58 +0800 Subject: [PATCH 23/47] Removes the old video --- apps/docs/pages/guides/api.mdx | 15 ----------- .../pages/guides/api/rest/client-libs.mdx | 26 +++++++++---------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/apps/docs/pages/guides/api.mdx b/apps/docs/pages/guides/api.mdx index 8cd1bccf46b..8e45a1243c7 100644 --- a/apps/docs/pages/guides/api.mdx +++ b/apps/docs/pages/guides/api.mdx @@ -37,23 +37,8 @@ It provides everything you need from a CRUD API: - Works with Postgres Functions - Works with the Postgres security model - including Row Level Security, Roles, and Grants. -
- -
- ## GraphQL API [#graphql-api-overview] - - -GraphQL is in Beta, and may have breaking changes. It is only available on self-hosted setups and Supabase projects created after 28th March 2022. - - - GraphQL in Supabase works through [pg_graphql](https://supabase.com/blog/pg-graphql), an open source PostgreSQL extension for GraphQL. ## Realtime API [#realtime-api-overview] diff --git a/apps/docs/pages/guides/api/rest/client-libs.mdx b/apps/docs/pages/guides/api/rest/client-libs.mdx index 5c018f62efe..4b6272e1f6d 100644 --- a/apps/docs/pages/guides/api/rest/client-libs.mdx +++ b/apps/docs/pages/guides/api/rest/client-libs.mdx @@ -11,22 +11,22 @@ Supabase provides client libraries for the REST and Realtime APIs. Some librarie ## Official Libraries -| `Language` | `Documentation` | `Source Code` | -| --------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------- | -| Javascript/Typescript | [Docs](https://supabase.com/docs/reference/javascript/introduction) | [supabase-js](https://github.com/supabase/supabase-js) | -| Dart/Flutter | [Docs](https://supabase.com/docs/reference/dart/introduction) | [supabase-dart](https://github.com/supabase/supabase-dart) | +| `Language` | `Source Code` | `Documentation` | +| --------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------- | +| Javascript/Typescript | [supabase-js](https://github.com/supabase/supabase-js) | [Docs](https://supabase.com/docs/reference/javascript/introduction) | +| Dart/Flutter | [supabase-dart](https://github.com/supabase/supabase-dart) | [Docs](https://supabase.com/docs/reference/dart/introduction) | ## Community Libraries -| `Language` | `Source Code` | -| ----------------------- | -------------------------------------------------------------------------------- | -| C# | [supabase-js](https://github.com/supabase-community/supabase-csharp) | -| Go | [supabase-go](https://github.com/supabase-community/supabase-go) | -| Kotlin | [supabase-kt](https://github.com/supabase-community/supabase-kt) | -| Python | [supabase-py](https://github.com/supabase-community/supabase-py) | -| Ruby | [supabase-rb](https://github.com/supabase-community/supabase-rb) | -| Swift | [supabase-swift](https://github.com/supabase-community/supabase-swift) | -| Godot Engine (GDScript) | [supabase-gdscript](https://github.com/supabase-community/godot-engine.supabase) | +| `Language` | `Source Code` | `Documentation` | +| ----------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------- | +| C# | [supabase-js](https://github.com/supabase-community/supabase-csharp) | [Docs](https://supabase.com/docs/reference/csharp/introduction) | +| Go | [supabase-go](https://github.com/supabase-community/supabase-go) | | +| Kotlin | [supabase-kt](https://github.com/supabase-community/supabase-kt) | | +| Python | [supabase-py](https://github.com/supabase-community/supabase-py) | [Docs](https://supabase.com/docs/reference/python/initializing) | +| Ruby | [supabase-rb](https://github.com/supabase-community/supabase-rb) | | +| Swift | [supabase-swift](https://github.com/supabase-community/supabase-swift) | | +| Godot Engine (GDScript) | [supabase-gdscript](https://github.com/supabase-community/godot-engine.supabase) | | export const Page = ({ children }) => From 1e6477bf1142259e5599153ae23b69525a1df5cd Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Fri, 24 Mar 2023 15:16:44 -0230 Subject: [PATCH 24/47] Move useParams to common packages --- packages/common/index.tsx | 1 + .../formatter/ForeignKeyFormatter.tsx | 3 ++- .../components/header/RLSBannerWarning.tsx | 3 ++- .../grid/components/header/RefreshButton.tsx | 2 +- .../interfaces/Auth/Policies/Policies.tsx | 3 ++- .../Billing/AddOns/ComputeSizeSelection.tsx | 3 ++- .../Billing/AddOns/CustomDomainSelection.tsx | 3 ++- .../Billing/AddOns/PITRDurationSelection.tsx | 3 ++- .../interfaces/Billing/AddOns/SupportPlan.tsx | 2 +- .../Billing/Subscription/Subscription.tsx | 3 ++- .../Database/Backups/PITR/PITRNotice.tsx | 3 ++- .../Database/Extensions/Extensions.tsx | 3 ++- .../Database/Wrappers/CreateWrapper.tsx | 3 ++- .../Database/Wrappers/EditWrapper.tsx | 3 ++- .../Database/Wrappers/WrapperRow.tsx | 3 ++- .../Wrappers/WrappersDisabledState.tsx | 3 ++- .../Database/Wrappers/WrappersDropdown.tsx | 3 ++- .../interfaces/Docs/GeneratingTypes.tsx | 3 ++- .../EdgeFunctionDetails.tsx | 3 ++- .../Functions/EdgeFunctionsListItem.tsx | 3 ++- .../Functions/TerminalInstructions.tsx | 2 +- .../Home/NewProjectPanel/APIKeys.tsx | 3 ++- .../interfaces/Home/ProjectUsage.tsx | 2 +- .../interfaces/Home/ProjectUsageSection.tsx | 2 +- .../BillingSettings/BillingEmail.tsx | 3 ++- .../BillingSettings/BillingSettings.tsx | 3 ++- .../GeneralSettings/GeneralSettings.tsx | 3 ++- .../TeamSettings/InviteMemberButton.tsx | 3 ++- .../TeamSettings/MemberActions.tsx | 3 ++- .../Organization/TeamSettings/MembersView.tsx | 3 ++- .../TeamSettings/TeamSettings.tsx | 3 ++- .../components/interfaces/Reports/Reports.tsx | 3 ++- .../interfaces/Settings/API/JWTSettings.tsx | 3 ++- .../Settings/API/PostgrestConfig.tsx | 3 ++- .../interfaces/Settings/API/ServiceList.tsx | 3 ++- .../DatabaseSettings/ResetDbPassword.tsx | 3 ++- .../AddRestrictionModal.tsx | 3 ++- .../NetworkRestrictions/AllowAllModal.tsx | 3 ++- .../NetworkRestrictions/DisallowAllModal.tsx | 3 ++- .../NetworkRestrictions.tsx | 3 ++- .../RemoveRestrictionModal.tsx | 3 ++- .../Settings/Database/SSLConfiguration.tsx | 3 ++- .../CustomDomainConfig/CustomDomainConfig.tsx | 3 ++- .../CustomDomainsConfigureHostname.tsx | 3 ++- .../General/Infrastructure/Infrastructure.tsx | 3 ++- .../ProjectUpgradeAlert.tsx | 3 ++- .../Settings/Logs/LogsPreviewer.tsx | 2 +- .../Vault/Keys/EncryptionKeysManagement.tsx | 3 ++- .../Settings/Vault/Secrets/SecretRow.tsx | 3 ++- .../Vault/Secrets/SecretsManagement.tsx | 3 ++- .../interfaces/Settings/Vault/VaultToggle.tsx | 3 ++- .../ColumnEditor/ColumnEditor.tsx | 3 ++- .../TableGridEditor/TableGridEditor.tsx | 3 ++- studio/components/layouts/FunctionsLayout.tsx | 11 +++++----- .../LayoutHeader/LayoutHeader.tsx | 3 ++- .../NavigationBar/NavigationBar.tsx | 2 +- .../layouts/ProjectLayout/ProjectContext.tsx | 3 ++- .../layouts/ProjectLayout/ProjectLayout.tsx | 3 ++- .../UpgradingState/UpgradingState.tsx | 3 ++- .../layouts/StorageLayout/StorageLayout.tsx | 3 ++- .../layouts/StorageLayout/StorageMenu.tsx | 3 ++- .../TableEditorLayout/TableEditorLayout.tsx | 3 ++- .../TableEditorLayout/TableEditorMenu.tsx | 3 ++- .../Docs/Pages/Tables/Introduction.tsx | 2 +- .../ui/OveragesBanner/OveragesBanner.tsx | 2 +- .../ui/ProjectSettings/DisplayApiSettings.tsx | 3 ++- .../ProjectSettings/DisplayConfigSettings.tsx | 2 +- .../ui/ProjectUpgradeFailedBanner.tsx | 2 +- studio/components/ui/UpgradeToPro.tsx | 3 ++- studio/hooks/index.ts | 1 - studio/hooks/misc/useParams.ts | 22 ------------------- studio/hooks/misc/useUpgradePrompt.tsx | 2 +- studio/hooks/misc/withAuth.tsx | 3 ++- studio/pages/new/[slug].tsx | 3 ++- studio/pages/org/[slug]/billing.tsx | 3 ++- studio/pages/org/[slug]/general.tsx | 3 ++- studio/pages/org/[slug]/invoices.tsx | 3 ++- studio/pages/org/[slug]/team.tsx | 3 ++- studio/pages/project/[ref]/api/graphiql.tsx | 3 ++- studio/pages/project/[ref]/api/index.tsx | 3 ++- studio/pages/project/[ref]/auth/policies.tsx | 3 ++- studio/pages/project/[ref]/editor/[id].tsx | 3 ++- .../[ref]/functions/[functionSlug]/index.tsx | 3 ++- .../functions/[functionSlug]/invocations.tsx | 2 +- .../[ref]/functions/[functionSlug]/logs.tsx | 2 +- .../pages/project/[ref]/functions/index.tsx | 2 +- studio/pages/project/[ref]/index.tsx | 3 ++- .../project/[ref]/logs/explorer/index.tsx | 3 ++- .../project/[ref]/reports/api-overview.tsx | 4 ++-- .../pages/project/[ref]/reports/database.tsx | 3 ++- studio/pages/project/[ref]/reports/index.tsx | 3 ++- studio/pages/project/[ref]/settings/api.tsx | 3 ++- .../pages/project/[ref]/settings/database.tsx | 3 ++- .../pages/project/[ref]/settings/storage.tsx | 3 ++- .../project/[ref]/settings/vault/keys.tsx | 3 ++- .../project/[ref]/settings/vault/secrets.tsx | 3 ++- .../project/[ref]/storage/buckets/index.tsx | 3 ++- .../pages/projects/LogsPreviewer.test.js | 2 +- .../tests/pages/projects/logs-query.test.js | 2 +- 99 files changed, 180 insertions(+), 124 deletions(-) delete mode 100644 studio/hooks/misc/useParams.ts diff --git a/packages/common/index.tsx b/packages/common/index.tsx index c33bdfbdfa3..8c30d73ee18 100644 --- a/packages/common/index.tsx +++ b/packages/common/index.tsx @@ -4,6 +4,7 @@ import SidebarLayout from './layouts/SidebarLayout' export * from './Button' export * from './Providers' +export * from './hooks' export const Layouts = { SidebarLayout, diff --git a/studio/components/grid/components/formatter/ForeignKeyFormatter.tsx b/studio/components/grid/components/formatter/ForeignKeyFormatter.tsx index 8b458b02882..2b5980c198e 100644 --- a/studio/components/grid/components/formatter/ForeignKeyFormatter.tsx +++ b/studio/components/grid/components/formatter/ForeignKeyFormatter.tsx @@ -5,7 +5,8 @@ import { SupaRow } from '../../types' import { NullValue } from '../common' import * as Tooltip from '@radix-ui/react-tooltip' import { Button, IconArrowRight } from 'ui' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' export const ForeignKeyFormatter = (props: PropsWithChildren>) => { const { ref, id } = useParams() diff --git a/studio/components/grid/components/header/RLSBannerWarning.tsx b/studio/components/grid/components/header/RLSBannerWarning.tsx index 9d5e16f422f..d694cb55111 100644 --- a/studio/components/grid/components/header/RLSBannerWarning.tsx +++ b/studio/components/grid/components/header/RLSBannerWarning.tsx @@ -1,7 +1,8 @@ import { useState } from 'react' import { Button, IconAlertCircle, IconExternalLink, Modal } from 'ui' import Link from 'next/link' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import ConfirmationModal from 'components/ui/ConfirmationModal' import type { PostgresTable } from '@supabase/postgres-meta' diff --git a/studio/components/grid/components/header/RefreshButton.tsx b/studio/components/grid/components/header/RefreshButton.tsx index dea181a3de8..979318340b6 100644 --- a/studio/components/grid/components/header/RefreshButton.tsx +++ b/studio/components/grid/components/header/RefreshButton.tsx @@ -1,7 +1,7 @@ import { useQueryClient, useIsFetching } from '@tanstack/react-query' import { SupaTable } from 'components/grid/types' import { sqlKeys } from 'data/sql/keys' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import { useEffect, useState } from 'react' import { Button, IconCheck, IconRefreshCw } from 'ui' import { SupabaseGridQueue } from '../../constants' diff --git a/studio/components/interfaces/Auth/Policies/Policies.tsx b/studio/components/interfaces/Auth/Policies/Policies.tsx index e587a5c5a77..17e208dba11 100644 --- a/studio/components/interfaces/Auth/Policies/Policies.tsx +++ b/studio/components/interfaces/Auth/Policies/Policies.tsx @@ -3,7 +3,8 @@ import { isEmpty } from 'lodash' import { IconHelpCircle } from 'ui' import { useRouter } from 'next/router' import { observer } from 'mobx-react-lite' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { PolicyEditorModal, PolicyTableRow } from 'components/interfaces/Auth/Policies' import type { PostgresRole, PostgresTable } from '@supabase/postgres-meta' diff --git a/studio/components/interfaces/Billing/AddOns/ComputeSizeSelection.tsx b/studio/components/interfaces/Billing/AddOns/ComputeSizeSelection.tsx index 3354fc5be98..437e7d152c1 100644 --- a/studio/components/interfaces/Billing/AddOns/ComputeSizeSelection.tsx +++ b/studio/components/interfaces/Billing/AddOns/ComputeSizeSelection.tsx @@ -2,7 +2,8 @@ import { FC } from 'react' import Link from 'next/link' import { Badge, Button, IconExternalLink, Radio } from 'ui' -import { useFlag, useParams } from 'hooks' +import { useFlag } from 'hooks' +import { useParams } from 'common/hooks' import { getProductPrice } from '../Billing.utils' import DisabledWarningDueToIncident from 'components/ui/DisabledWarningDueToIncident' import { SubscriptionAddon } from './AddOns.types' diff --git a/studio/components/interfaces/Billing/AddOns/CustomDomainSelection.tsx b/studio/components/interfaces/Billing/AddOns/CustomDomainSelection.tsx index 8e5515013f9..022940f66cd 100644 --- a/studio/components/interfaces/Billing/AddOns/CustomDomainSelection.tsx +++ b/studio/components/interfaces/Billing/AddOns/CustomDomainSelection.tsx @@ -2,7 +2,8 @@ import Link from 'next/link' import { FC } from 'react' import { Badge, Button, Radio } from 'ui' -import { useFlag, useParams } from 'hooks' +import { useFlag } from 'hooks' +import { useParams } from 'common/hooks' import { getProductPrice } from '../Billing.utils' import DisabledWarningDueToIncident from 'components/ui/DisabledWarningDueToIncident' import { SubscriptionAddon } from './AddOns.types' diff --git a/studio/components/interfaces/Billing/AddOns/PITRDurationSelection.tsx b/studio/components/interfaces/Billing/AddOns/PITRDurationSelection.tsx index 88609d60601..52476a92bd4 100644 --- a/studio/components/interfaces/Billing/AddOns/PITRDurationSelection.tsx +++ b/studio/components/interfaces/Billing/AddOns/PITRDurationSelection.tsx @@ -2,7 +2,8 @@ import Link from 'next/link' import { FC } from 'react' import { Badge, IconAlertCircle, Radio, Button } from 'ui' -import { useFlag, useParams, useStore } from 'hooks' +import { useFlag, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { SubscriptionAddon } from './AddOns.types' import { getProductPrice } from '../Billing.utils' import DisabledWarningDueToIncident from 'components/ui/DisabledWarningDueToIncident' diff --git a/studio/components/interfaces/Billing/AddOns/SupportPlan.tsx b/studio/components/interfaces/Billing/AddOns/SupportPlan.tsx index 56f22afce68..5fa2c3af196 100644 --- a/studio/components/interfaces/Billing/AddOns/SupportPlan.tsx +++ b/studio/components/interfaces/Billing/AddOns/SupportPlan.tsx @@ -1,7 +1,7 @@ import { FC } from 'react' import Link from 'next/link' import { Button } from 'ui' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' interface Props { currentOption?: any diff --git a/studio/components/interfaces/Billing/Subscription/Subscription.tsx b/studio/components/interfaces/Billing/Subscription/Subscription.tsx index 4bf3bf8ad04..7adcbd7ab79 100644 --- a/studio/components/interfaces/Billing/Subscription/Subscription.tsx +++ b/studio/components/interfaces/Billing/Subscription/Subscription.tsx @@ -5,7 +5,8 @@ import { Button, Loading } from 'ui' import * as Tooltip from '@radix-ui/react-tooltip' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useFlag, useParams } from 'hooks' +import { checkPermissions, useFlag } from 'hooks' +import { useParams } from 'common/hooks' import { STRIPE_PRODUCT_IDS } from 'lib/constants' import { formatBytes } from 'lib/helpers' diff --git a/studio/components/interfaces/Database/Backups/PITR/PITRNotice.tsx b/studio/components/interfaces/Database/Backups/PITR/PITRNotice.tsx index daab4106406..52ac8f1212b 100644 --- a/studio/components/interfaces/Database/Backups/PITR/PITRNotice.tsx +++ b/studio/components/interfaces/Database/Backups/PITR/PITRNotice.tsx @@ -2,7 +2,8 @@ import Link from 'next/link' import { Button, IconCalendar } from 'ui' import { FormPanel } from 'components/ui/Forms' import * as Tooltip from '@radix-ui/react-tooltip' -import { checkPermissions, useParams } from 'hooks' +import { checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query' import { getPITRRetentionDuration } from './PITR.utils' import { PermissionAction } from '@supabase/shared-types/out/constants' diff --git a/studio/components/interfaces/Database/Extensions/Extensions.tsx b/studio/components/interfaces/Database/Extensions/Extensions.tsx index d53fc0d3026..48164cf331f 100644 --- a/studio/components/interfaces/Database/Extensions/Extensions.tsx +++ b/studio/components/interfaces/Database/Extensions/Extensions.tsx @@ -4,7 +4,8 @@ import { partition, isNull } from 'lodash' import { Input, IconSearch, IconAlertCircle, Button, IconBookOpen } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { useStore, checkPermissions, useParams } from 'hooks' +import { useStore, checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import ExtensionCard from './ExtensionCard' import { HIDDEN_EXTENSIONS } from './Extensions.constants' import NoSearchResults from 'components/ui/NoSearchResults' diff --git a/studio/components/interfaces/Database/Wrappers/CreateWrapper.tsx b/studio/components/interfaces/Database/Wrappers/CreateWrapper.tsx index 242345a01f7..558873bb1e3 100644 --- a/studio/components/interfaces/Database/Wrappers/CreateWrapper.tsx +++ b/studio/components/interfaces/Database/Wrappers/CreateWrapper.tsx @@ -6,7 +6,8 @@ import { observer } from 'mobx-react-lite' import { Button, Form, Input, IconArrowLeft, IconExternalLink, IconEdit, IconTrash } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useParams, useStore } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { FormPanel, FormActions, diff --git a/studio/components/interfaces/Database/Wrappers/EditWrapper.tsx b/studio/components/interfaces/Database/Wrappers/EditWrapper.tsx index fdcec1aa4b8..a8661b6f3bf 100644 --- a/studio/components/interfaces/Database/Wrappers/EditWrapper.tsx +++ b/studio/components/interfaces/Database/Wrappers/EditWrapper.tsx @@ -16,7 +16,8 @@ import { import { PermissionAction } from '@supabase/shared-types/out/constants' import { VaultSecret } from 'types' -import { checkPermissions, useImmutableValue, useParams, useStore } from 'hooks' +import { checkPermissions, useImmutableValue, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { useFDWsQuery } from 'data/fdw/fdws-query' import { useFDWUpdateMutation } from 'data/fdw/fdw-update-mutation' diff --git a/studio/components/interfaces/Database/Wrappers/WrapperRow.tsx b/studio/components/interfaces/Database/Wrappers/WrapperRow.tsx index 896cd11d45e..f91e933aad5 100644 --- a/studio/components/interfaces/Database/Wrappers/WrapperRow.tsx +++ b/studio/components/interfaces/Database/Wrappers/WrapperRow.tsx @@ -6,7 +6,8 @@ import * as Tooltip from '@radix-ui/react-tooltip' import { PermissionAction } from '@supabase/shared-types/out/constants' import { Collapsible, IconChevronUp, Button, IconExternalLink, IconTrash, IconEdit } from 'ui' -import { useParams, useStore, checkPermissions } from 'hooks' +import { useStore, checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import { WrapperMeta } from './Wrappers.types' import { FDW } from 'data/fdw/fdws-query' import { useFDWDeleteMutation } from 'data/fdw/fdw-delete-mutation' diff --git a/studio/components/interfaces/Database/Wrappers/WrappersDisabledState.tsx b/studio/components/interfaces/Database/Wrappers/WrappersDisabledState.tsx index d80ab7d57c5..94b1f88a96a 100644 --- a/studio/components/interfaces/Database/Wrappers/WrappersDisabledState.tsx +++ b/studio/components/interfaces/Database/Wrappers/WrappersDisabledState.tsx @@ -5,7 +5,8 @@ import { observer } from 'mobx-react-lite' import * as Tooltip from '@radix-ui/react-tooltip' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useParams, useStore } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { BASE_PATH } from 'lib/constants' const WrappersDisabledState = () => { diff --git a/studio/components/interfaces/Database/Wrappers/WrappersDropdown.tsx b/studio/components/interfaces/Database/Wrappers/WrappersDropdown.tsx index 76a5ce5b6b4..ac87a6afc00 100644 --- a/studio/components/interfaces/Database/Wrappers/WrappersDropdown.tsx +++ b/studio/components/interfaces/Database/Wrappers/WrappersDropdown.tsx @@ -6,7 +6,8 @@ import { Button, Dropdown, IconPlus } from 'ui' import * as Tooltip from '@radix-ui/react-tooltip' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { useParams, checkPermissions } from 'hooks' +import { checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import { WRAPPERS } from './Wrappers.constants' interface Props { diff --git a/studio/components/interfaces/Docs/GeneratingTypes.tsx b/studio/components/interfaces/Docs/GeneratingTypes.tsx index 9cdabfe4ecc..e44e997c541 100644 --- a/studio/components/interfaces/Docs/GeneratingTypes.tsx +++ b/studio/components/interfaces/Docs/GeneratingTypes.tsx @@ -4,7 +4,8 @@ import React from 'react' import { Button, IconDownload, IconExternalLink } from 'ui' import { API_ADMIN_URL } from 'lib/constants' import { get } from 'lib/common/fetch' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import CodeSnippet from 'components/to-be-cleaned/Docs/CodeSnippet' interface Props { diff --git a/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx b/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx index af520a18d31..67926ca0715 100644 --- a/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx +++ b/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx @@ -17,7 +17,8 @@ import { IconExternalLink, } from 'ui' -import { useStore, useParams, checkPermissions } from 'hooks' +import { useStore, checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import Panel from 'components/ui/Panel' import CommandRender from '../CommandRender' import { generateCLICommands } from './EdgeFunctionDetails.utils' diff --git a/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx b/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx index bb1b1f86edd..5d045e63303 100644 --- a/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx +++ b/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx @@ -5,7 +5,8 @@ import { observer } from 'mobx-react-lite' import { IconCheck, IconClipboard } from 'ui' import * as Tooltip from '@radix-ui/react-tooltip' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import Table from 'components/to-be-cleaned/Table' import { EdgeFunctionsResponse } from 'data/edge-functions/edge-functions-query' diff --git a/studio/components/interfaces/Functions/TerminalInstructions.tsx b/studio/components/interfaces/Functions/TerminalInstructions.tsx index a5770812ef0..9582f069448 100644 --- a/studio/components/interfaces/Functions/TerminalInstructions.tsx +++ b/studio/components/interfaces/Functions/TerminalInstructions.tsx @@ -1,7 +1,7 @@ import CommandRender from 'components/interfaces/Functions/CommandRender' import { useAccessTokensQuery } from 'data/access-tokens/access-tokens-query' import { useProjectApiQuery } from 'data/config/project-api-query' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import Link from 'next/link' import { useRouter } from 'next/router' import { FC, useState } from 'react' diff --git a/studio/components/interfaces/Home/NewProjectPanel/APIKeys.tsx b/studio/components/interfaces/Home/NewProjectPanel/APIKeys.tsx index a2fc1baf0dd..42b39dbac4b 100644 --- a/studio/components/interfaces/Home/NewProjectPanel/APIKeys.tsx +++ b/studio/components/interfaces/Home/NewProjectPanel/APIKeys.tsx @@ -6,7 +6,8 @@ import { IconAlertCircle, IconLoader, Input } from 'ui' import { useProjectApiQuery } from 'data/config/project-api-query' import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query' -import { checkPermissions, useParams } from 'hooks' +import { checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import Snippets from 'components/to-be-cleaned/Docs/Snippets' import Panel from 'components/ui/Panel' import SimpleCodeBlock from 'components/to-be-cleaned/SimpleCodeBlock' diff --git a/studio/components/interfaces/Home/ProjectUsage.tsx b/studio/components/interfaces/Home/ProjectUsage.tsx index d113c2bd5c9..ff7ffc79c9b 100644 --- a/studio/components/interfaces/Home/ProjectUsage.tsx +++ b/studio/components/interfaces/Home/ProjectUsage.tsx @@ -7,7 +7,7 @@ import ChartHandler from 'components/to-be-cleaned/Charts/ChartHandler' import Panel from 'components/ui/Panel' import { DATE_FORMAT, METRICS } from 'lib/constants' import { ChartIntervals } from 'types' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import { useProjectLogStatsQuery } from 'data/logs/project-log-stats-query' const CHART_INTERVALS: ChartIntervals[] = [ diff --git a/studio/components/interfaces/Home/ProjectUsageSection.tsx b/studio/components/interfaces/Home/ProjectUsageSection.tsx index 9064717135c..df3811a5e14 100644 --- a/studio/components/interfaces/Home/ProjectUsageSection.tsx +++ b/studio/components/interfaces/Home/ProjectUsageSection.tsx @@ -2,7 +2,7 @@ import { FC } from 'react' import { observer } from 'mobx-react-lite' import { IconLoader, IconAlertCircle } from 'ui' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import { ProjectUsage, NewProjectPanel } from 'components/interfaces/Home' import InformationBox from 'components/ui/InformationBox' import { ProjectUsageResponseUsageKeys, useProjectUsageQuery } from 'data/usage/project-usage-query' diff --git a/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx b/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx index b9b25b40ede..db8b8d7b2fa 100644 --- a/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx +++ b/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx @@ -1,7 +1,8 @@ import { useEffect } from 'react' import { Form, Input } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { useStore, checkPermissions, useParams } from 'hooks' +import { useStore, checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import { API_URL } from 'lib/constants' import { patch } from 'lib/common/fetch' import { FormActions, FormPanel, FormSection, FormSectionContent } from 'components/ui/Forms' diff --git a/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx b/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx index cd7cfb91c0f..ba01a03ef37 100644 --- a/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx +++ b/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx @@ -2,7 +2,8 @@ import { useState, useEffect } from 'react' import { observer } from 'mobx-react-lite' import { Project } from 'types' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { API_URL } from 'lib/constants' import { get } from 'lib/common/fetch' diff --git a/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx b/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx index 5910d5043d6..848b15c8227 100644 --- a/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx +++ b/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx @@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite' import { Form, Input } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { useStore, checkPermissions, useParams } from 'hooks' +import { useStore, checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import { API_URL } from 'lib/constants' import { patch } from 'lib/common/fetch' diff --git a/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx b/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx index 8dd437ebe3d..15a6394cf15 100644 --- a/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx +++ b/studio/components/interfaces/Organization/TeamSettings/InviteMemberButton.tsx @@ -5,7 +5,8 @@ import * as Tooltip from '@radix-ui/react-tooltip' import { Button, Form, IconMail, Input, Modal, Select } from 'ui' import { Member, Role } from 'types' -import { checkPermissions, useParams, useStore } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { PermissionAction } from '@supabase/shared-types/out/constants' import { useOrganizationMemberInviteCreateMutation } from 'data/organizations/organization-member-invite-create-mutation' diff --git a/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx b/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx index 9bd55624df0..7251a7863b2 100644 --- a/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx +++ b/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx @@ -5,7 +5,8 @@ import { FC } from 'react' import { Button, Dropdown, IconMoreHorizontal, IconTrash } from 'ui' import { confirmAlert } from 'components/to-be-cleaned/ModalsDeprecated/ConfirmModal' -import { checkPermissions, useParams, useStore } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { Member, Role } from 'types' import { useOrganizationMemberDeleteMutation } from 'data/organizations/organization-member-delete-mutation' diff --git a/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx b/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx index 7958d26f3f7..506693eb0e2 100644 --- a/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx +++ b/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx @@ -5,7 +5,8 @@ import { observer } from 'mobx-react-lite' import { Badge, Button, Loading, Listbox, IconUser, Modal, IconAlertCircle, IconLoader } from 'ui' import { Member } from 'types' -import { useStore, useParams } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import ShimmeringLoader from 'components/ui/ShimmeringLoader' import Table from 'components/to-be-cleaned/Table' diff --git a/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx b/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx index fe2a3ae0e24..4e088c3acb6 100644 --- a/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx +++ b/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx @@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite' import { Button, Input, IconSearch } from 'ui' import * as Tooltip from '@radix-ui/react-tooltip' -import { useStore, useParams } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { post } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { useProfileQuery } from 'data/profile/profile-query' diff --git a/studio/components/interfaces/Reports/Reports.tsx b/studio/components/interfaces/Reports/Reports.tsx index ec43b8041ec..6cf14f25a4c 100644 --- a/studio/components/interfaces/Reports/Reports.tsx +++ b/studio/components/interfaces/Reports/Reports.tsx @@ -16,7 +16,8 @@ import { } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useParams } from 'hooks' +import { checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import { uuidv4 } from 'lib/helpers' import { METRIC_CATEGORIES, METRICS, TIME_PERIODS_REPORTS } from 'lib/constants' import { useProjectContentStore } from 'stores/projectContentStore' diff --git a/studio/components/interfaces/Settings/API/JWTSettings.tsx b/studio/components/interfaces/Settings/API/JWTSettings.tsx index f05341b37cf..61e4107fec6 100644 --- a/studio/components/interfaces/Settings/API/JWTSettings.tsx +++ b/studio/components/interfaces/Settings/API/JWTSettings.tsx @@ -23,7 +23,8 @@ import { import { PermissionAction } from '@supabase/shared-types/out/constants' import { uuidv4 } from 'lib/helpers' -import { useStore, checkPermissions, useParams } from 'hooks' +import { useStore, checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query' import { useJwtSecretUpdateMutation } from 'data/config/jwt-secret-update-mutation' import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query' diff --git a/studio/components/interfaces/Settings/API/PostgrestConfig.tsx b/studio/components/interfaces/Settings/API/PostgrestConfig.tsx index 1d3f6d74d74..2ed7d977be9 100644 --- a/studio/components/interfaces/Settings/API/PostgrestConfig.tsx +++ b/studio/components/interfaces/Settings/API/PostgrestConfig.tsx @@ -4,7 +4,8 @@ import { observer } from 'mobx-react-lite' import { Input, Form, IconAlertCircle, InputNumber } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useStore, useParams } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query' import { useProjectPostgrestConfigUpdateMutation } from 'data/config/project-postgrest-config-update-mutation' import MultiSelect from 'components/ui/MultiSelect' diff --git a/studio/components/interfaces/Settings/API/ServiceList.tsx b/studio/components/interfaces/Settings/API/ServiceList.tsx index 3209ae9ae15..f22b65c0215 100644 --- a/studio/components/interfaces/Settings/API/ServiceList.tsx +++ b/studio/components/interfaces/Settings/API/ServiceList.tsx @@ -3,7 +3,8 @@ import { JwtSecretUpdateError, JwtSecretUpdateStatus } from '@supabase/shared-ty import { useEffect, useRef } from 'react' import { IconAlertCircle, Input } from 'ui' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query' import { useProjectApiQuery } from 'data/config/project-api-query' diff --git a/studio/components/interfaces/Settings/Database/DatabaseSettings/ResetDbPassword.tsx b/studio/components/interfaces/Settings/Database/DatabaseSettings/ResetDbPassword.tsx index 3d77f371349..7e2b38770e9 100644 --- a/studio/components/interfaces/Settings/Database/DatabaseSettings/ResetDbPassword.tsx +++ b/studio/components/interfaces/Settings/Database/DatabaseSettings/ResetDbPassword.tsx @@ -5,7 +5,8 @@ import generator from 'generate-password' import { Input, Button, Modal } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useParams, useStore } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { patch } from 'lib/common/fetch' import { passwordStrength } from 'lib/helpers' import { API_URL, DEFAULT_MINIMUM_PASSWORD_STRENGTH } from 'lib/constants' diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx index c2cfd9755f4..b89a4a0f116 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx @@ -3,7 +3,8 @@ import { Address4 } from 'ip-address' import { Button, Form, IconHelpCircle, Input, Modal } from 'ui' import * as Tooltip from '@radix-ui/react-tooltip' -import { useStore, useParams } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import InformationBox from 'components/ui/InformationBox' import { checkIfPrivate, getAddressEndRange } from './NetworkRestrictions.utils' import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation' diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AllowAllModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AllowAllModal.tsx index 084d12c3d89..880c176f9bb 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AllowAllModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AllowAllModal.tsx @@ -1,7 +1,8 @@ import { FC, useState } from 'react' import { Modal, Button } from 'ui' -import { useStore, useParams } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation' interface Props { diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx index 537d509515e..c843de5dcbc 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx @@ -1,7 +1,8 @@ import { FC, useState } from 'react' import { Modal, Button } from 'ui' -import { useStore, useParams } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation' import InformationBox from 'components/ui/InformationBox' diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.tsx index b3c47c59119..b12b9ce3ea6 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.tsx @@ -4,7 +4,8 @@ import * as Tooltip from '@radix-ui/react-tooltip' import { Button, IconAlertCircle, IconExternalLink, IconGlobe, IconLock } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useParams } from 'hooks' +import { checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import Panel from 'components/ui/Panel' import { FormPanel, FormHeader } from 'components/ui/Forms' import ShimmeringLoader from 'components/ui/ShimmeringLoader' diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/RemoveRestrictionModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/RemoveRestrictionModal.tsx index cd2a4f7f654..c0aa9253ea7 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/RemoveRestrictionModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/RemoveRestrictionModal.tsx @@ -1,7 +1,8 @@ import { FC, useState } from 'react' import { Alert, Button, Form, Input, Modal } from 'ui' -import { useStore, useParams } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation' interface Props { diff --git a/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx b/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx index 9f1f8596905..fabf4d0fc7a 100644 --- a/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx +++ b/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx @@ -4,7 +4,8 @@ import * as Tooltip from '@radix-ui/react-tooltip' import { Button, IconDownload, Toggle, IconLoader, Alert } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useParams, useStore, useFlag } from 'hooks' +import { checkPermissions, useStore, useFlag } from 'hooks' +import { useParams } from 'common/hooks' import { FormHeader, FormPanel, diff --git a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx index 261746b733d..26c6b9372e6 100644 --- a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx +++ b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx @@ -2,7 +2,8 @@ import Link from 'next/link' import { observer } from 'mobx-react-lite' import { IconAlertCircle } from 'ui' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { useProjectApiQuery } from 'data/config/project-api-query' import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query' import Panel from 'components/ui/Panel' diff --git a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx index 398f62b9ec3..c41ae2dd05f 100644 --- a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx +++ b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx @@ -4,7 +4,8 @@ import { observer } from 'mobx-react-lite' import { Button, Form, IconExternalLink, Input } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useParams, useStore } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { FormActions, FormPanel, diff --git a/studio/components/interfaces/Settings/General/Infrastructure/Infrastructure.tsx b/studio/components/interfaces/Settings/General/Infrastructure/Infrastructure.tsx index e80035da084..14e84a6611f 100644 --- a/studio/components/interfaces/Settings/General/Infrastructure/Infrastructure.tsx +++ b/studio/components/interfaces/Settings/General/Infrastructure/Infrastructure.tsx @@ -4,7 +4,8 @@ import { Alert, Badge, Button, IconPackage, Input } from 'ui' import { observer } from 'mobx-react-lite' import * as Tooltip from '@radix-ui/react-tooltip' -import { useFlag, useParams, useStore } from 'hooks' +import { useFlag, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants' import { FormHeader, diff --git a/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx b/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx index 559e60eaafa..9b400574b83 100644 --- a/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx +++ b/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx @@ -12,7 +12,8 @@ import { IconExternalLink, } from 'ui' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { post } from 'lib/common/fetch' import { API_ADMIN_URL, PROJECT_STATUS } from 'lib/constants' import InformationBox from 'components/ui/InformationBox' diff --git a/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx b/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx index 543c32821d8..1d718bcfb33 100644 --- a/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx +++ b/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx @@ -22,7 +22,7 @@ import ShimmerLine from 'components/ui/ShimmerLine' import LoadingOpacity from 'components/ui/LoadingOpacity' import { useUpgradePrompt } from 'hooks/misc/useUpgradePrompt' import UpgradePrompt from './UpgradePrompt' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query' /** diff --git a/studio/components/interfaces/Settings/Vault/Keys/EncryptionKeysManagement.tsx b/studio/components/interfaces/Settings/Vault/Keys/EncryptionKeysManagement.tsx index 63cac0d9820..41508ea7e88 100644 --- a/studio/components/interfaces/Settings/Vault/Keys/EncryptionKeysManagement.tsx +++ b/studio/components/interfaces/Settings/Vault/Keys/EncryptionKeysManagement.tsx @@ -18,7 +18,8 @@ import { IconExternalLink, } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { useParams, useStore, checkPermissions } from 'hooks' +import { useStore, checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import Divider from 'components/ui/Divider' const DEFAULT_KEY_NAME = 'No description provided' diff --git a/studio/components/interfaces/Settings/Vault/Secrets/SecretRow.tsx b/studio/components/interfaces/Settings/Vault/Secrets/SecretRow.tsx index c3c08a02e92..998d026cd70 100644 --- a/studio/components/interfaces/Settings/Vault/Secrets/SecretRow.tsx +++ b/studio/components/interfaces/Settings/Vault/Secrets/SecretRow.tsx @@ -16,7 +16,8 @@ import { import * as Tooltip from '@radix-ui/react-tooltip' import { PermissionAction } from '@supabase/shared-types/out/constants' import { VaultSecret } from 'types' -import { useParams, useStore, checkPermissions } from 'hooks' +import { useStore, checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' interface Props { secret: VaultSecret diff --git a/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx b/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx index 05ca9c5d11c..e242d1ddf12 100644 --- a/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx +++ b/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx @@ -5,7 +5,8 @@ import { FC, Fragment, useState, useEffect } from 'react' import { IconSearch, Input, Button, Listbox, IconLoader, IconExternalLink, IconX } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { useStore, useParams, checkPermissions } from 'hooks' +import { useStore, checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import SecretRow from './SecretRow' import EditSecretModal from './EditSecretModal' import DeleteSecretModal from './DeleteSecretModal' diff --git a/studio/components/interfaces/Settings/Vault/VaultToggle.tsx b/studio/components/interfaces/Settings/Vault/VaultToggle.tsx index 4f7c679b2ba..599da240ef3 100644 --- a/studio/components/interfaces/Settings/Vault/VaultToggle.tsx +++ b/studio/components/interfaces/Settings/Vault/VaultToggle.tsx @@ -5,7 +5,8 @@ import * as Tooltip from '@radix-ui/react-tooltip' import { Button, IconExternalLink } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useParams, useStore } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { BASE_PATH } from 'lib/constants' interface Props {} diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnEditor.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnEditor.tsx index c5097858d28..79d492764e6 100644 --- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnEditor.tsx +++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnEditor.tsx @@ -10,7 +10,8 @@ import type { PostgresType, } from '@supabase/postgres-meta' -import { useFlag, useParams, useStore } from 'hooks' +import { useFlag, useStore } from 'hooks' +import { useParams } from 'common/hooks' import ActionBar from '../ActionBar' import HeaderTitle from './HeaderTitle' import ColumnType from './ColumnType' diff --git a/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx b/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx index 5cd00a26d62..a8520df38ba 100644 --- a/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx +++ b/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx @@ -7,7 +7,8 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { QueryKey, useQueryClient } from '@tanstack/react-query' import { SchemaView } from 'types' -import { checkPermissions, useFlag, useStore, useParams } from 'hooks' +import { checkPermissions, useFlag, useStore } from 'hooks' +import { useParams } from 'common/hooks' import GridHeaderActions from './GridHeaderActions' import NotFoundState from './NotFoundState' import SidePanelEditor from './SidePanelEditor' diff --git a/studio/components/layouts/FunctionsLayout.tsx b/studio/components/layouts/FunctionsLayout.tsx index d7f4cc6a7de..c7271b1d877 100644 --- a/studio/components/layouts/FunctionsLayout.tsx +++ b/studio/components/layouts/FunctionsLayout.tsx @@ -3,7 +3,8 @@ import { FC, ReactNode, useState } from 'react' import { observer } from 'mobx-react-lite' import { Button, IconExternalLink, IconCode, Modal, IconTerminal } from 'ui' -import { checkPermissions, useParams, withAuth } from 'hooks' +import { checkPermissions, withAuth } from 'hooks' +import { useParams } from 'common/hooks' import FunctionsNav from '../interfaces/Functions/FunctionsNav' import BaseLayout from 'components/layouts' import NoPermission from 'components/ui/NoPermission' @@ -44,11 +45,11 @@ const FunctionsLayout: FC = ({ title, children }) => { <>
diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx index c7c599a6073..3abe0aead41 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx @@ -2,7 +2,8 @@ import Link from 'next/link' import { observer } from 'mobx-react-lite' import { IS_PLATFORM, PRICING_TIER_PRODUCT_IDS } from 'lib/constants' -import { useFlag, useParams, useStore } from 'hooks' +import { useFlag, useStore } from 'hooks' +import { useParams } from 'common/hooks' import BreadcrumbsView from './BreadcrumbsView' import OrgDropdown from './OrgDropdown' import ProjectDropdown from './ProjectDropdown' diff --git a/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx b/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx index 599436d22ac..3d534b80502 100644 --- a/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx +++ b/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx @@ -13,7 +13,7 @@ import { generateToolRoutes, } from './NavigationBar.utils' import NavigationIconButton from './NavigationIconButton' -import { useParams } from 'hooks/misc/useParams' +import { useParams } from 'common/hooks' interface Props {} diff --git a/studio/components/layouts/ProjectLayout/ProjectContext.tsx b/studio/components/layouts/ProjectLayout/ProjectContext.tsx index 0c7b2144f31..aa78d0ac3ad 100644 --- a/studio/components/layouts/ProjectLayout/ProjectContext.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectContext.tsx @@ -1,4 +1,5 @@ -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { observer } from 'mobx-react-lite' import { createContext, PropsWithChildren, useContext, useMemo } from 'react' import { Project } from 'types' diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index 8169fd225be..71a37032b0f 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -2,7 +2,8 @@ import Head from 'next/head' import { FC, ReactNode, PropsWithChildren, Fragment } from 'react' import { observer } from 'mobx-react-lite' import { useRouter } from 'next/router' -import { useStore, withAuth, useFlag, useParams } from 'hooks' +import { useStore, withAuth, useFlag } from 'hooks' +import { useParams } from 'common/hooks' import { PROJECT_STATUS } from 'lib/constants' import Connecting from 'components/ui/Loading' diff --git a/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx b/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx index ddee8e564a7..1d72fbc11a7 100644 --- a/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx +++ b/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx @@ -15,7 +15,8 @@ import { } from 'ui' import { DatabaseUpgradeStatus } from '@supabase/shared-types/out/events' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { DATABASE_UPGRADE_MESSAGES } from './UpgradingState.constants' import { useProjectUpgradingStatusQuery } from 'data/config/project-upgrade-status-query' diff --git a/studio/components/layouts/StorageLayout/StorageLayout.tsx b/studio/components/layouts/StorageLayout/StorageLayout.tsx index cb46bbb60d2..45cc14d9aa1 100644 --- a/studio/components/layouts/StorageLayout/StorageLayout.tsx +++ b/studio/components/layouts/StorageLayout/StorageLayout.tsx @@ -3,7 +3,8 @@ import { useRouter } from 'next/router' import { find, filter, get as _get } from 'lodash' import { observer } from 'mobx-react-lite' -import { useParams, useStore, withAuth } from 'hooks' +import { useStore, withAuth } from 'hooks' +import { useParams } from 'common/hooks' import { AutoApiService, useProjectApiQuery } from 'data/config/project-api-query' import BaseLayout from 'components/layouts' import ProjectLayout from '../ProjectLayout/ProjectLayout' diff --git a/studio/components/layouts/StorageLayout/StorageMenu.tsx b/studio/components/layouts/StorageLayout/StorageMenu.tsx index 1d2a88de2fd..dc955a1af57 100644 --- a/studio/components/layouts/StorageLayout/StorageMenu.tsx +++ b/studio/components/layouts/StorageLayout/StorageMenu.tsx @@ -6,7 +6,8 @@ import * as Tooltip from '@radix-ui/react-tooltip' import { Button, Menu, IconLoader, Alert, IconEdit } from 'ui' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useParams } from 'hooks' +import { checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import BucketRow from './BucketRow' import { useStorageStore } from 'localStores/storageExplorer/StorageExplorerStore' diff --git a/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx b/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx index bd25b07380e..09e50969b71 100644 --- a/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx +++ b/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx @@ -4,7 +4,8 @@ import { noop } from 'lodash' import { observer } from 'mobx-react-lite' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useParams, useStore } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { Entity } from 'data/entity-types/entity-type-query' import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants' import ProjectLayout from '../ProjectLayout/ProjectLayout' diff --git a/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx b/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx index 088984868c0..1ee4a969b8f 100644 --- a/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx +++ b/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx @@ -22,7 +22,8 @@ import * as Tooltip from '@radix-ui/react-tooltip' import { PermissionAction } from '@supabase/shared-types/out/constants' import type { PostgresSchema } from '@supabase/postgres-meta' -import { checkPermissions, useStore, useParams } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import ProductMenuItem from 'components/ui/ProductMenu/ProductMenuItem' import { useEntityTypesQuery } from 'data/entity-types/entity-types-infinite-query' import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants' diff --git a/studio/components/to-be-cleaned/Docs/Pages/Tables/Introduction.tsx b/studio/components/to-be-cleaned/Docs/Pages/Tables/Introduction.tsx index ba2441ad011..28723badb85 100644 --- a/studio/components/to-be-cleaned/Docs/Pages/Tables/Introduction.tsx +++ b/studio/components/to-be-cleaned/Docs/Pages/Tables/Introduction.tsx @@ -1,6 +1,6 @@ import { FC } from 'react' import Link from 'next/link' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import CodeSnippet from '../../CodeSnippet' import GeneratingTypes from 'components/interfaces/Docs/GeneratingTypes' diff --git a/studio/components/ui/OveragesBanner/OveragesBanner.tsx b/studio/components/ui/OveragesBanner/OveragesBanner.tsx index 991c9a05b86..5b0d0b73edf 100644 --- a/studio/components/ui/OveragesBanner/OveragesBanner.tsx +++ b/studio/components/ui/OveragesBanner/OveragesBanner.tsx @@ -2,7 +2,7 @@ import Link from 'next/link' import { FC } from 'react' import { Alert, Button } from 'ui' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants' import { getResourcesApproachingLimits, getResourcesExceededLimits } from './OveragesBanner.utils' import { useProjectUsageQuery } from 'data/usage/project-usage-query' diff --git a/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx b/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx index 587e742a41a..e1bebe6e641 100644 --- a/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx +++ b/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx @@ -5,7 +5,8 @@ import { IconAlertCircle, IconLoader, Input } from 'ui' import Panel from 'components/ui/Panel' import { useProjectSettingsQuery } from 'data/config/project-settings-query' import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query' -import { checkPermissions, useParams } from 'hooks' +import { checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import { DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants' const DisplayApiSettings = () => { diff --git a/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx b/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx index 70f1c5eba57..5c36f018f4f 100644 --- a/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx +++ b/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx @@ -2,7 +2,7 @@ import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events' import Panel from 'components/ui/Panel' import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query' import { useProjectSettingsQuery } from 'data/config/project-settings-query' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import { DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants' import { FC } from 'react' import { IconAlertCircle, IconLoader, Input } from 'ui' diff --git a/studio/components/ui/ProjectUpgradeFailedBanner.tsx b/studio/components/ui/ProjectUpgradeFailedBanner.tsx index 5e23e6634e2..d2531452f71 100644 --- a/studio/components/ui/ProjectUpgradeFailedBanner.tsx +++ b/studio/components/ui/ProjectUpgradeFailedBanner.tsx @@ -2,7 +2,7 @@ import dayjs from 'dayjs' import Link from 'next/link' import { useState } from 'react' import { Alert, Button, IconX } from 'ui' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import { useProjectUpgradingStatusQuery } from 'data/config/project-upgrade-status-query' import { DatabaseUpgradeStatus } from '@supabase/shared-types/out/events' diff --git a/studio/components/ui/UpgradeToPro.tsx b/studio/components/ui/UpgradeToPro.tsx index ccee10b1295..c5ccdfeedd2 100644 --- a/studio/components/ui/UpgradeToPro.tsx +++ b/studio/components/ui/UpgradeToPro.tsx @@ -4,7 +4,8 @@ import Link from 'next/link' import { FC, ReactNode } from 'react' import * as Tooltip from '@radix-ui/react-tooltip' -import { checkPermissions, useStore, useFlag, useParams } from 'hooks' +import { checkPermissions, useStore, useFlag } from 'hooks' +import { useParams } from 'common/hooks' import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants' import { PermissionAction } from '@supabase/shared-types/out/constants' diff --git a/studio/hooks/index.ts b/studio/hooks/index.ts index 5995d22e059..724d06ec3df 100644 --- a/studio/hooks/index.ts +++ b/studio/hooks/index.ts @@ -2,7 +2,6 @@ export * from './misc/useStore' export * from './misc/withAuth' export * from './misc/useOptimisticSqlSnippetCreate' export * from './misc/useImmutableValue' -export * from './misc/useParams' export * from './misc/useCheckPermissions' export * from './deprecated' export * from './ui' diff --git a/studio/hooks/misc/useParams.ts b/studio/hooks/misc/useParams.ts deleted file mode 100644 index cac30143bd9..00000000000 --- a/studio/hooks/misc/useParams.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useRouter } from 'next/router' -import { useMemo } from 'react' - -export function useParams(): { - [k: string]: string | undefined -} { - const { query } = useRouter() - - return useMemo( - () => - Object.fromEntries( - Object.entries(query).map(([key, value]) => { - if (Array.isArray(value)) { - return [key, value[0]] - } else { - return [key, value] - } - }) - ), - [query] - ) -} diff --git a/studio/hooks/misc/useUpgradePrompt.tsx b/studio/hooks/misc/useUpgradePrompt.tsx index f19f090c302..f58aca0c45d 100644 --- a/studio/hooks/misc/useUpgradePrompt.tsx +++ b/studio/hooks/misc/useUpgradePrompt.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import { maybeShowUpgradePrompt } from 'components/interfaces/Settings/Logs' import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query' diff --git a/studio/hooks/misc/withAuth.tsx b/studio/hooks/misc/withAuth.tsx index 6d4a54a0e01..37ad66dac49 100644 --- a/studio/hooks/misc/withAuth.tsx +++ b/studio/hooks/misc/withAuth.tsx @@ -3,7 +3,8 @@ import { useRouter } from 'next/router' import { ComponentType, useEffect } from 'react' import { usePermissionsQuery } from 'data/permissions/permissions-query' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { useAuth } from 'lib/auth' import { IS_PLATFORM } from 'lib/constants' import { getReturnToPath, STORAGE_KEY } from 'lib/gotrue' diff --git a/studio/pages/new/[slug].tsx b/studio/pages/new/[slug].tsx index 52b630ad0a2..67305391615 100644 --- a/studio/pages/new/[slug].tsx +++ b/studio/pages/new/[slug].tsx @@ -21,7 +21,8 @@ import { PRICING_TIER_FREE_KEY, PRICING_TIER_PRODUCT_IDS, } from 'lib/constants' -import { useStore, useFlag, withAuth, checkPermissions, useParams } from 'hooks' +import { useStore, useFlag, withAuth, checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-limit-check-query' import { WizardLayoutWithoutAuth } from 'components/layouts' diff --git a/studio/pages/org/[slug]/billing.tsx b/studio/pages/org/[slug]/billing.tsx index b8510f913f3..83c44735b40 100644 --- a/studio/pages/org/[slug]/billing.tsx +++ b/studio/pages/org/[slug]/billing.tsx @@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite' import { Tabs } from 'ui' import { NextPageWithLayout } from 'types' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import Loading from 'components/ui/Loading' import { OrganizationLayout } from 'components/layouts' import { BillingSettings } from 'components/interfaces/Organization' diff --git a/studio/pages/org/[slug]/general.tsx b/studio/pages/org/[slug]/general.tsx index 43cd730e8a2..f07415ba47e 100644 --- a/studio/pages/org/[slug]/general.tsx +++ b/studio/pages/org/[slug]/general.tsx @@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite' import { Tabs } from 'ui' import { NextPageWithLayout } from 'types' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import Loading from 'components/ui/Loading' import { OrganizationLayout } from 'components/layouts' import { GeneralSettings } from 'components/interfaces/Organization' diff --git a/studio/pages/org/[slug]/invoices.tsx b/studio/pages/org/[slug]/invoices.tsx index f6ee9ee7eae..5ccf1ebc825 100644 --- a/studio/pages/org/[slug]/invoices.tsx +++ b/studio/pages/org/[slug]/invoices.tsx @@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite' import { Tabs } from 'ui' import { NextPageWithLayout } from 'types' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import Loading from 'components/ui/Loading' import { OrganizationLayout } from 'components/layouts' import { InvoicesSettings } from 'components/interfaces/Organization' diff --git a/studio/pages/org/[slug]/team.tsx b/studio/pages/org/[slug]/team.tsx index f90d16a6486..7f96623cd61 100644 --- a/studio/pages/org/[slug]/team.tsx +++ b/studio/pages/org/[slug]/team.tsx @@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite' import { Tabs } from 'ui' import { NextPageWithLayout } from 'types' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import Loading from 'components/ui/Loading' import { OrganizationLayout } from 'components/layouts' import { TeamSettings } from 'components/interfaces/Organization' diff --git a/studio/pages/project/[ref]/api/graphiql.tsx b/studio/pages/project/[ref]/api/graphiql.tsx index 6fe1b70347c..f5ea6ee64d7 100644 --- a/studio/pages/project/[ref]/api/graphiql.tsx +++ b/studio/pages/project/[ref]/api/graphiql.tsx @@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite' import { createGraphiQLFetcher } from '@graphiql/toolkit' import { NextPageWithLayout } from 'types' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { API_URL } from 'lib/constants' import ExtensionCard from 'components/interfaces/Database/Extensions/ExtensionCard' import GraphiQL from 'components/interfaces/GraphQL/GraphiQL' diff --git a/studio/pages/project/[ref]/api/index.tsx b/studio/pages/project/[ref]/api/index.tsx index 3ceb55c3383..a890cfd8de3 100644 --- a/studio/pages/project/[ref]/api/index.tsx +++ b/studio/pages/project/[ref]/api/index.tsx @@ -3,7 +3,8 @@ import { FC, createContext, useContext, useEffect, useState } from 'react' import { observer, useLocalObservable } from 'mobx-react-lite' import { NextPageWithLayout } from 'types' -import { checkPermissions, useParams, useStore } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { snakeToCamel } from 'lib/helpers' import { useProjectApiQuery } from 'data/config/project-api-query' import { DocsLayout } from 'components/layouts' diff --git a/studio/pages/project/[ref]/auth/policies.tsx b/studio/pages/project/[ref]/auth/policies.tsx index 3d4b440d580..5fe4fc3171b 100644 --- a/studio/pages/project/[ref]/auth/policies.tsx +++ b/studio/pages/project/[ref]/auth/policies.tsx @@ -6,7 +6,8 @@ import { PostgresTable, PostgresPolicy } from '@supabase/postgres-meta' import { PermissionAction } from '@supabase/shared-types/out/constants' import { NextPageWithLayout } from 'types' -import { checkPermissions, useParams, useStore } from 'hooks' +import { checkPermissions, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { AuthLayout } from 'components/layouts' import { Policies } from 'components/interfaces/Auth/Policies' import NoPermission from 'components/ui/NoPermission' diff --git a/studio/pages/project/[ref]/editor/[id].tsx b/studio/pages/project/[ref]/editor/[id].tsx index 9fd9ca0e2dc..f9c3996736a 100644 --- a/studio/pages/project/[ref]/editor/[id].tsx +++ b/studio/pages/project/[ref]/editor/[id].tsx @@ -7,7 +7,8 @@ import { isUndefined, isNaN } from 'lodash' import { Alert, Button, Checkbox, IconExternalLink, Modal } from 'ui' import type { PostgresTable, PostgresColumn } from '@supabase/postgres-meta' -import { useStore, withAuth, useUrlState, useParams } from 'hooks' +import { useStore, withAuth, useUrlState } from 'hooks' +import { useParams } from 'common/hooks' import { entityTypeKeys } from 'data/entity-types/keys' import { Entity } from 'data/entity-types/entity-type-query' import { Dictionary } from 'components/grid' diff --git a/studio/pages/project/[ref]/functions/[functionSlug]/index.tsx b/studio/pages/project/[ref]/functions/[functionSlug]/index.tsx index 156a1da2897..403f83ceec8 100644 --- a/studio/pages/project/[ref]/functions/[functionSlug]/index.tsx +++ b/studio/pages/project/[ref]/functions/[functionSlug]/index.tsx @@ -5,7 +5,8 @@ import { Button } from 'ui' import { observer } from 'mobx-react-lite' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { checkPermissions, useParams } from 'hooks' +import { checkPermissions } from 'hooks' +import { useParams } from 'common/hooks' import { ChartIntervals, NextPageWithLayout } from 'types' import { DATE_FORMAT } from 'lib/constants' import Panel from 'components/ui/Panel' diff --git a/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx b/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx index 3fc638aa7c2..16e179787e9 100644 --- a/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx +++ b/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx @@ -1,7 +1,7 @@ import { observer } from 'mobx-react-lite' import { NextPageWithLayout } from 'types' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import LogsPreviewer from 'components/interfaces/Settings/Logs/LogsPreviewer' import FunctionsLayout from 'components/layouts/FunctionsLayout' import { useEdgeFunctionQuery } from 'data/edge-functions/edge-function-query' diff --git a/studio/pages/project/[ref]/functions/[functionSlug]/logs.tsx b/studio/pages/project/[ref]/functions/[functionSlug]/logs.tsx index fd9510aee15..404985c654a 100644 --- a/studio/pages/project/[ref]/functions/[functionSlug]/logs.tsx +++ b/studio/pages/project/[ref]/functions/[functionSlug]/logs.tsx @@ -1,6 +1,6 @@ import { observer } from 'mobx-react-lite' import { NextPageWithLayout } from 'types' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import LogsPreviewer from 'components/interfaces/Settings/Logs/LogsPreviewer' import FunctionsLayout from 'components/layouts/FunctionsLayout' import { useEdgeFunctionQuery } from 'data/edge-functions/edge-function-query' diff --git a/studio/pages/project/[ref]/functions/index.tsx b/studio/pages/project/[ref]/functions/index.tsx index e78b41e0996..0673931084f 100644 --- a/studio/pages/project/[ref]/functions/index.tsx +++ b/studio/pages/project/[ref]/functions/index.tsx @@ -1,6 +1,6 @@ import { observer } from 'mobx-react-lite' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import { NextPageWithLayout } from 'types' import Table from 'components/to-be-cleaned/Table' import FunctionsLayout from 'components/layouts/FunctionsLayout' diff --git a/studio/pages/project/[ref]/index.tsx b/studio/pages/project/[ref]/index.tsx index 38d0c25de3e..a3bce4646b6 100644 --- a/studio/pages/project/[ref]/index.tsx +++ b/studio/pages/project/[ref]/index.tsx @@ -1,6 +1,7 @@ import { observer } from 'mobx-react-lite' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { NextPageWithLayout } from 'types' import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants' import { ProjectLayoutWithAuth } from 'components/layouts' diff --git a/studio/pages/project/[ref]/logs/explorer/index.tsx b/studio/pages/project/[ref]/logs/explorer/index.tsx index a781fb52c3b..a45ba94cdc3 100644 --- a/studio/pages/project/[ref]/logs/explorer/index.tsx +++ b/studio/pages/project/[ref]/logs/explorer/index.tsx @@ -4,7 +4,8 @@ import { useRouter } from 'next/router' import { observer } from 'mobx-react-lite' import { Input, Modal, Form, Button } from 'ui' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import useLogsQuery from 'hooks/analytics/useLogsQuery' import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query' import { NextPageWithLayout, UserContent } from 'types' diff --git a/studio/pages/project/[ref]/reports/api-overview.tsx b/studio/pages/project/[ref]/reports/api-overview.tsx index 00c45d7888b..9a2a9f8a60c 100644 --- a/studio/pages/project/[ref]/reports/api-overview.tsx +++ b/studio/pages/project/[ref]/reports/api-overview.tsx @@ -18,10 +18,10 @@ import ReportHeader from 'components/interfaces/Reports/ReportHeader' import { DatePickerToFrom, LogsEndpointParams } from 'components/interfaces/Settings/Logs' import ReportFilterBar from 'components/interfaces/Reports/ReportFilterBar' import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' import { isEqual } from 'lodash' import ShimmerLine from 'components/ui/ShimmerLine' -import ReportPadding from "components/interfaces/Reports/ReportPadding" +import ReportPadding from 'components/interfaces/Reports/ReportPadding' export const ApiReport: NextPageWithLayout = () => { const { ref: projectRef } = useParams() diff --git a/studio/pages/project/[ref]/reports/database.tsx b/studio/pages/project/[ref]/reports/database.tsx index 51ff8dfb946..ae7c16394cb 100644 --- a/studio/pages/project/[ref]/reports/database.tsx +++ b/studio/pages/project/[ref]/reports/database.tsx @@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite' import { FC, useEffect, useState } from 'react' import { Badge, Button, IconArrowRight, IconExternalLink } from 'ui' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { PRICING_TIER_PRODUCT_IDS, TIME_PERIODS_INFRA, diff --git a/studio/pages/project/[ref]/reports/index.tsx b/studio/pages/project/[ref]/reports/index.tsx index aece220eb06..dfda77f3db6 100644 --- a/studio/pages/project/[ref]/reports/index.tsx +++ b/studio/pages/project/[ref]/reports/index.tsx @@ -4,7 +4,8 @@ import { useRouter } from 'next/router' import { PermissionAction } from '@supabase/shared-types/out/constants' import { NextPageWithLayout } from 'types' -import { checkPermissions, useFlag, useParams, useStore } from 'hooks' +import { checkPermissions, useFlag, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { post } from 'lib/common/fetch' import { API_URL, PROJECT_STATUS } from 'lib/constants' import { useProjectContentStore } from 'stores/projectContentStore' diff --git a/studio/pages/project/[ref]/settings/api.tsx b/studio/pages/project/[ref]/settings/api.tsx index 288127c97a6..0446e3d361a 100644 --- a/studio/pages/project/[ref]/settings/api.tsx +++ b/studio/pages/project/[ref]/settings/api.tsx @@ -2,7 +2,8 @@ import { createContext } from 'react' import { useRouter } from 'next/router' import { observer, useLocalObservable } from 'mobx-react-lite' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { NextPageWithLayout } from 'types' import { SettingsLayout } from 'components/layouts' import ServiceList from 'components/interfaces/Settings/API/ServiceList' diff --git a/studio/pages/project/[ref]/settings/database.tsx b/studio/pages/project/[ref]/settings/database.tsx index 026f211e571..d9b90a1cecb 100644 --- a/studio/pages/project/[ref]/settings/database.tsx +++ b/studio/pages/project/[ref]/settings/database.tsx @@ -1,6 +1,7 @@ import { observer } from 'mobx-react-lite' import { NextPageWithLayout } from 'types' -import { useFlag, useParams } from 'hooks' +import { useFlag } from 'hooks' +import { useParams } from 'common/hooks' import { SettingsLayout } from 'components/layouts' import { ConnectionPooling, diff --git a/studio/pages/project/[ref]/settings/storage.tsx b/studio/pages/project/[ref]/settings/storage.tsx index 7f2f2c15180..0ac1df2ea45 100644 --- a/studio/pages/project/[ref]/settings/storage.tsx +++ b/studio/pages/project/[ref]/settings/storage.tsx @@ -2,7 +2,8 @@ import React, { useEffect } from 'react' import { observer } from 'mobx-react-lite' import { API_URL, PROJECT_STATUS } from 'lib/constants' -import { useFlag, useParams, useStore } from 'hooks' +import { useFlag, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { post } from 'lib/common/fetch' import { SettingsLayout } from 'components/layouts' import { StorageSettings } from 'components/to-be-cleaned/Storage' diff --git a/studio/pages/project/[ref]/settings/vault/keys.tsx b/studio/pages/project/[ref]/settings/vault/keys.tsx index 530c5b2fb3d..1b14ead4e9a 100644 --- a/studio/pages/project/[ref]/settings/vault/keys.tsx +++ b/studio/pages/project/[ref]/settings/vault/keys.tsx @@ -3,7 +3,8 @@ import { useRouter } from 'next/router' import { observer } from 'mobx-react-lite' import { Tabs } from 'ui' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { NextPageWithLayout } from 'types' import { SettingsLayout } from 'components/layouts' import { VaultToggle, EncryptionKeysManagement } from 'components/interfaces/Settings/Vault' diff --git a/studio/pages/project/[ref]/settings/vault/secrets.tsx b/studio/pages/project/[ref]/settings/vault/secrets.tsx index 4a7a92ebb33..8b50c447ac0 100644 --- a/studio/pages/project/[ref]/settings/vault/secrets.tsx +++ b/studio/pages/project/[ref]/settings/vault/secrets.tsx @@ -3,7 +3,8 @@ import { useRouter } from 'next/router' import { observer } from 'mobx-react-lite' import { Tabs } from 'ui' -import { useParams, useStore } from 'hooks' +import { useStore } from 'hooks' +import { useParams } from 'common/hooks' import { NextPageWithLayout } from 'types' import { SettingsLayout } from 'components/layouts' import { VaultToggle, SecretsManagement } from 'components/interfaces/Settings/Vault' diff --git a/studio/pages/project/[ref]/storage/buckets/index.tsx b/studio/pages/project/[ref]/storage/buckets/index.tsx index 396a914ba2a..8f4ad3bba21 100644 --- a/studio/pages/project/[ref]/storage/buckets/index.tsx +++ b/studio/pages/project/[ref]/storage/buckets/index.tsx @@ -3,7 +3,8 @@ import { useRouter } from 'next/router' import { observer } from 'mobx-react-lite' import { API_URL } from 'lib/constants' -import { useFlag, useParams, useStore } from 'hooks' +import { useFlag, useStore } from 'hooks' +import { useParams } from 'common/hooks' import { post } from 'lib/common/fetch' import { PROJECT_STATUS } from 'lib/constants' import { StorageLayout } from 'components/layouts' diff --git a/studio/tests/pages/projects/LogsPreviewer.test.js b/studio/tests/pages/projects/LogsPreviewer.test.js index 15774d0b66c..52fa59e3742 100644 --- a/studio/tests/pages/projects/LogsPreviewer.test.js +++ b/studio/tests/pages/projects/LogsPreviewer.test.js @@ -23,7 +23,7 @@ import userEvent from '@testing-library/user-event' import { wait } from '@testing-library/user-event/dist/utils' import { logDataFixture } from '../../fixtures' import { LogsTableName } from 'components/interfaces/Settings/Logs' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' beforeEach(() => { // reset mocks between tests diff --git a/studio/tests/pages/projects/logs-query.test.js b/studio/tests/pages/projects/logs-query.test.js index 378b62c6f02..62221916a67 100644 --- a/studio/tests/pages/projects/logs-query.test.js +++ b/studio/tests/pages/projects/logs-query.test.js @@ -8,7 +8,7 @@ import { logDataFixture } from '../../fixtures' import { clickDropdown } from 'tests/helpers' import dayjs from 'dayjs' import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query' -import { useParams } from 'hooks' +import { useParams } from 'common/hooks' const defaultRouterMock = () => { const router = jest.fn() From cf8153506cd5e5d1d3c1fdd74b674a6fb632dfc9 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Fri, 24 Mar 2023 15:32:03 -0230 Subject: [PATCH 25/47] Add new useParams file --- packages/common/hooks/index.ts | 3 +++ packages/common/hooks/useParams.ts | 22 ++++++++++++++++++++++ packages/ui/src/utils/useParams.ts | 22 ++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 packages/common/hooks/index.ts create mode 100644 packages/common/hooks/useParams.ts create mode 100644 packages/ui/src/utils/useParams.ts diff --git a/packages/common/hooks/index.ts b/packages/common/hooks/index.ts new file mode 100644 index 00000000000..6e0d89b50b7 --- /dev/null +++ b/packages/common/hooks/index.ts @@ -0,0 +1,3 @@ +import React from 'react' + +export * from './useParams' diff --git a/packages/common/hooks/useParams.ts b/packages/common/hooks/useParams.ts new file mode 100644 index 00000000000..cac30143bd9 --- /dev/null +++ b/packages/common/hooks/useParams.ts @@ -0,0 +1,22 @@ +import { useRouter } from 'next/router' +import { useMemo } from 'react' + +export function useParams(): { + [k: string]: string | undefined +} { + const { query } = useRouter() + + return useMemo( + () => + Object.fromEntries( + Object.entries(query).map(([key, value]) => { + if (Array.isArray(value)) { + return [key, value[0]] + } else { + return [key, value] + } + }) + ), + [query] + ) +} diff --git a/packages/ui/src/utils/useParams.ts b/packages/ui/src/utils/useParams.ts new file mode 100644 index 00000000000..cac30143bd9 --- /dev/null +++ b/packages/ui/src/utils/useParams.ts @@ -0,0 +1,22 @@ +import { useRouter } from 'next/router' +import { useMemo } from 'react' + +export function useParams(): { + [k: string]: string | undefined +} { + const { query } = useRouter() + + return useMemo( + () => + Object.fromEntries( + Object.entries(query).map(([key, value]) => { + if (Array.isArray(value)) { + return [key, value[0]] + } else { + return [key, value] + } + }) + ), + [query] + ) +} From b7c0ba26ccc02731b50494e6aa96913269b56a99 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Fri, 24 Mar 2023 16:00:00 -0230 Subject: [PATCH 26/47] Remove dupe file --- packages/ui/src/utils/useParams.ts | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 packages/ui/src/utils/useParams.ts diff --git a/packages/ui/src/utils/useParams.ts b/packages/ui/src/utils/useParams.ts deleted file mode 100644 index cac30143bd9..00000000000 --- a/packages/ui/src/utils/useParams.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useRouter } from 'next/router' -import { useMemo } from 'react' - -export function useParams(): { - [k: string]: string | undefined -} { - const { query } = useRouter() - - return useMemo( - () => - Object.fromEntries( - Object.entries(query).map(([key, value]) => { - if (Array.isArray(value)) { - return [key, value[0]] - } else { - return [key, value] - } - }) - ), - [query] - ) -} From 91a9da82ddaa38ce30fb7795789bef40b2a05864 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Fri, 24 Mar 2023 16:08:02 -0230 Subject: [PATCH 27/47] Add new test mock for common/hooks --- studio/tests/pages/projects/LogsPreviewer.test.js | 1 + studio/tests/pages/projects/logs-query.test.js | 1 + 2 files changed, 2 insertions(+) diff --git a/studio/tests/pages/projects/LogsPreviewer.test.js b/studio/tests/pages/projects/LogsPreviewer.test.js index 52fa59e3742..780a82f9d4a 100644 --- a/studio/tests/pages/projects/LogsPreviewer.test.js +++ b/studio/tests/pages/projects/LogsPreviewer.test.js @@ -23,6 +23,7 @@ import userEvent from '@testing-library/user-event' import { wait } from '@testing-library/user-event/dist/utils' import { logDataFixture } from '../../fixtures' import { LogsTableName } from 'components/interfaces/Settings/Logs' +jest.mock('common/hooks') import { useParams } from 'common/hooks' beforeEach(() => { diff --git a/studio/tests/pages/projects/logs-query.test.js b/studio/tests/pages/projects/logs-query.test.js index 62221916a67..eb1ce4d5c50 100644 --- a/studio/tests/pages/projects/logs-query.test.js +++ b/studio/tests/pages/projects/logs-query.test.js @@ -8,6 +8,7 @@ import { logDataFixture } from '../../fixtures' import { clickDropdown } from 'tests/helpers' import dayjs from 'dayjs' import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query' +jest.mock('common/hooks') import { useParams } from 'common/hooks' const defaultRouterMock = () => { From 8efa0395c06b403eb46ad20c3d6dee242c47ef56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Kania-Orze=C5=82?= Date: Mon, 27 Mar 2023 13:11:24 +0200 Subject: [PATCH 28/47] readme(integrations): remove Dashibase, becase there is no longer active development from several months --- .../Navigation/Navigation.constants.ts | 1 - .../NavigationMenu.constants.ts | 1 - .../pages/guides/integrations/dashibase.mdx | 105 ------------------ apps/docs/public/sitemap.xml | 6 - 4 files changed, 113 deletions(-) delete mode 100644 apps/docs/pages/guides/integrations/dashibase.mdx diff --git a/apps/docs/components/Navigation/Navigation.constants.ts b/apps/docs/components/Navigation/Navigation.constants.ts index beb5bdc962d..f1bdd8c0662 100644 --- a/apps/docs/components/Navigation/Navigation.constants.ts +++ b/apps/docs/components/Navigation/Navigation.constants.ts @@ -383,7 +383,6 @@ export const menuItems: NavMenu = { url: undefined, items: [ { name: 'Appsmith', url: '/guides/integrations/appsmith', items: [] }, - { name: 'Dashibase', url: '/guides/integrations/dashibase', items: [] }, { name: 'DhiWise', url: '/guides/integrations/dhiwise', items: [] }, { name: 'Directus', url: '/guides/integrations/directus', items: [] }, { name: 'Draftbit', url: '/guides/integrations/draftbit', items: [] }, diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 366153f7b8e..a0d0ae5d354 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -850,7 +850,6 @@ export const integrations = { url: undefined, items: [ { name: 'Appsmith', url: '/guides/integrations/appsmith' }, - { name: 'Dashibase', url: '/guides/integrations/dashibase' }, { name: 'DhiWise', url: '/guides/integrations/dhiwise' }, { name: 'Directus', url: '/guides/integrations/directus' }, { name: 'Draftbit', url: '/guides/integrations/draftbit' }, diff --git a/apps/docs/pages/guides/integrations/dashibase.mdx b/apps/docs/pages/guides/integrations/dashibase.mdx deleted file mode 100644 index 962f1397b29..00000000000 --- a/apps/docs/pages/guides/integrations/dashibase.mdx +++ /dev/null @@ -1,105 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'dashibase', - title: 'Dashibase', - description: 'Create an internal CRUD dashboard with Supabase and Dashibase in 15 mins.', -} - -Are you tired of building admin panels? [Dashibase](https://dashibase.com/) is a super fast way to build internal CRUD dashboards with a Notion-like UI. - -This guide walks you through how to build a customer admin panel using Supabase on the backend and Dashibase on the front so that you and your team can easily find and update customer information. - -## Quick explanation - -Just so we are on the same page, here are some of the terms we use: - -- **Data source:** This is the data that powers your dashboard (i.e. your Supabase database). -- **Dashboard:** This is the friendly interface that you build on top of your Supabase database using Dashibase. -- **Page:** Each dashboard can have multiple pages. Each page can have multiple blocks of text or tables. - -Now that is out of the way, let's build our customer admin panel. - -(Start your stopwatch!) - -## Step 1. Connect Supabase to Dashibase - -First, in Dashibase, go to “Data Sources” via the left sidebar and click on “Connect data source” in the upper-right corner. - -Note: We do not download or store any of your database data. - -![Connect Supabase to Dashibase](/docs/img/guides/integrations/dashibase/connect-database.png) - -Enter your database credentials, which you can find in Supabase under Settings > Database > Connection info. For additional security, you can also download your SSL certificate and upload it to Dashibase. - -![Supabase credentials](/docs/img/guides/integrations/dashibase/supabase-credentials.png) - -## Step 2. Set up your data access policy - -Once you have connected your database, you can also control what your team can see and edit in your database via Dashibase. There are usually certain data that you don’t want others to edit or even see (e.g. address, email, identification number). - -Note: If you prefer to restrict access to your database _before_ you connect to Dashibase for security reasons, you can [create a restricted user](https://aws.amazon.com/blogs/database/managing-postgresql-users-and-roles/) via [Supabase's SQL Editor](/docs/guides/database#the-sql-editor). The slight advantage of doing this in Dashibase is that there is a GUI, which makes it easier to set up the access control (but we understand some developers might find it more secure to restrict the access themselves). - -![Set data access in Dashibase](/docs/img/guides/integrations/dashibase/set-data-access.png) - -You can even control which columns within a table that others can see and edit. In the example below, for the `tags` table, the `name` column can be read and updated, the `created_at` column can be read, and the `id` column cannot even be read. - -![Set column-level access in Dashibase](/docs/img/guides/integrations/dashibase/set-column-access.png) - -Once you are happy with the data access control, you can decide who in your organization can edit this data source (usually other developers) or use this data source to create dashboards. - -![Set user access to data source in Dashibase](/docs/img/guides/integrations/dashibase/data-source-user-access.png) - -## Step 3. Build your dashboard - -To create a dashboard, go to “Dashboards” via the left sidebar in Dashibase and click on “Create dashboard” in the upper-right corner. - -Here’s where the fun begins. There are many ways to build your dashboards in Dashibase using features such as markdown formatting and slash command. - -You will see some automatically-generated content in your dashboard so that you can set up your dashboard more quickly. For this example, let's clear them out and start from scratch. - -![Automatically-generated content for your dashboard](/docs/img/guides/integrations/dashibase/initial-dashboard-content.png) - -Using our Notion-like UI, you can quickly build out your dashboard through typing. For example, you can type '/table' to add a table from your database to your dashboard. No more dragging components across your screen and dropping them on a blank canvas repeatedly. - -![Automatically-generated content for your dashboard](/docs/img/guides/integrations/dashibase/slash-command.gif) - -If you have set up [foreign keys in your Supabase database](/docs/guides/database/tables#joining-tables-with-foreign-keys), you can easily show data from multiple tables on the table in your Dashibase dashboard. Simply click on the "Columns" button beside the "Filter" button and select the columns you want to show or hide. For this example, we want to show the customers' country and plan price, which are in separate `countries` and `plans` tables. - -![Setting up the Customers table](/docs/img/guides/integrations/dashibase/customers-table.png) - -For all the tables in your dashboard, you can rename the column headers, add filter and sort, adjust the column width, and more. - -You can also add text to provide context or instructions for the rest of your team as they are using your dashboard. This saves you the trouble of having your dashboard and documentation in separate places. - -![Customers table with instructions](/docs/img/guides/integrations/dashibase/customers-table-with-text.png) - -Most importantly, we automatically generate a single-item view for each of the items on your table so that you do not have to manually create it yourself. You can easily edit or rearrange the blocks just like in Notion. - -![Automatically generated single-item view](/docs/img/guides/integrations/dashibase/single-item-view.png) - -## Step 4. Share your dashboard with your team - -Finally, save and share the dashboard with your team. There are two levels of access: - -- Edit dashboard: Edit the text blocks, edit the tables, etc. -- Use dashboard: View and update the data via the dashboard - -Click on the “Share” button in the upper-right corner of your dashboard to share access: - -![Share dashboard access](/docs/img/guides/integrations/dashibase/share-dashboard.png) - -Now your team can use this customer admin panel to find and update customer information easily! - -P.S. How long did we take? :) - -## Resources - -- [Dashibase website](https://dashibase.com/) -- [Dashibase open-source repo](https://github.com/dashibase) -- [Dashibase Twitter](https://twitter.com/dashibase) -- [Dashibase Discord](https://discord.gg/crhDFYvbbq) - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/public/sitemap.xml b/apps/docs/public/sitemap.xml index 598060f55aa..4f90fe8fe89 100644 --- a/apps/docs/public/sitemap.xml +++ b/apps/docs/public/sitemap.xml @@ -312,12 +312,6 @@ 0.5 - - https://supabase.com/docs/guides/integrations/dashibase - weekly - 0.5 - - https://supabase.com/docs/guides/integrations/dhiwise weekly From 52aa2e5aa42ca99d5243efe7a59142495055023d Mon Sep 17 00:00:00 2001 From: aar2dee2 <85004512+aar2dee2@users.noreply.github.com> Date: Wed, 29 Mar 2023 11:27:54 +0530 Subject: [PATCH 29/47] Update outdated links to docs https://www.loom.com/share/933441316d7043ae861dd77d8620b3d5 --- spec/supabase_js_v2.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/supabase_js_v2.yml b/spec/supabase_js_v2.yml index 09cbcec793e..3e63efe3756 100644 --- a/spec/supabase_js_v2.yml +++ b/spec/supabase_js_v2.yml @@ -177,7 +177,7 @@ functions: - id: sign-up-with-redirect name: Sign up with a redirect URL description: | - - See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project. + - See [redirect URLs and wildcards](/docs/guides/auth#redirect-urls-and-wildcards) to add additional redirect URLs to your project. code: | ```js const { data, error } = await supabase.auth.signUp( @@ -232,7 +232,7 @@ functions: - If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP. - If you're using phone, you can configure whether you want the user to receive a OTP. - The magic link's destination URL is determined by the [`SITE_URL`](/docs/reference/auth/config#site_url). - - See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project. + - See [redirect URLs and wildcards](/docs/guides/auth#redirect-urls-and-wildcards) to add additional redirect URLs to your project. - Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](https://app.supabase.com/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`. examples: - id: sign-in-with-email @@ -263,7 +263,7 @@ functions: $ref: '@supabase/gotrue-js.GoTrueClient.signInWithOAuth' notes: | - This method is used for signing in using a third-party provider. - - Supabase supports many different [third-party providers](https://supabase.com/docs/guides/auth#providers). + - Supabase supports many different [third-party providers](https://supabase.com/docs/guides/auth#configure-third-party-providers). examples: - id: sign-in-using-a-third-party-provider name: Sign in using a third-party provider @@ -279,7 +279,7 @@ functions: isSpotlight: false description: | - When the third-party provider successfully authenticates the user, the provider redirects the user to the URL specified in the `redirectTo` parameter. This parameter defaults to the [`SITE_URL`](/docs/reference/auth/config#site_url). It does not redirect the user immediately after invoking this method. - - See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project. + - See [redirect URLs and wildcards](/docs/guides/auth#redirect-urls-and-wildcards) to add additional redirect URLs to your project. code: | ```js const { data, error } = await supabase.auth.signInWithOAuth({ @@ -753,7 +753,7 @@ functions: You can use [`onAuthStateChange()`](/docs/reference/javascript/auth-onauthstatechange) to listen and invoke a callback function on these events. - When the user clicks the reset link in the email they are redirected back to your application. You can configure the URL that the user is redirected to with the `redirectTo` parameter. - See [redirect URLs and wildcards](/docs/guides/auth/overview#redirect-urls-and-wildcards) to add additional redirect URLs to your project. + See [redirect URLs and wildcards](/docs/guides/auth#redirect-urls-and-wildcards) to add additional redirect URLs to your project. - After the user has been redirected successfully, prompt them for a new password and call `updateUser()`: ```js const { data, error } = await supabase.auth.updateUser({ From 41e3c9b75cebf103e6a725a0ced735752a393162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Kania-Orze=C5=82?= Date: Wed, 29 Mar 2023 09:44:19 +0200 Subject: [PATCH 30/47] chore(next-config): add redirect from removed Dashibase docs to docs homepage --- apps/docs/next.config.mjs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs index b3c71cae82e..0f74f99b170 100644 --- a/apps/docs/next.config.mjs +++ b/apps/docs/next.config.mjs @@ -88,6 +88,12 @@ const nextConfig = { basePath: false, permanent: false, }, + { + source: '/guides/integrations/dashibase', + destination: '/docs', + basePath: false, + permanent: false, + }, ] }, } From ef670011ea839cd32424b37c3b5d7846a6b87ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Kania-Orze=C5=82?= Date: Wed, 29 Mar 2023 09:58:15 +0200 Subject: [PATCH 31/47] chore(next-config): move redirect to proper place --- apps/docs/next.config.mjs | 6 ------ apps/www/lib/redirects.js | 5 +++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs index 0f74f99b170..b3c71cae82e 100644 --- a/apps/docs/next.config.mjs +++ b/apps/docs/next.config.mjs @@ -88,12 +88,6 @@ const nextConfig = { basePath: false, permanent: false, }, - { - source: '/guides/integrations/dashibase', - destination: '/docs', - basePath: false, - permanent: false, - }, ] }, } diff --git a/apps/www/lib/redirects.js b/apps/www/lib/redirects.js index fa1f28415f4..31955305fcb 100644 --- a/apps/www/lib/redirects.js +++ b/apps/www/lib/redirects.js @@ -1892,4 +1892,9 @@ module.exports = [ source: '/docs/guides/realtime/postgres-changes', destination: '/docs/guides/realtime/extensions/postgres-changes', }, + { + permanent: true, + source: '/docs/guides/integrations/dashibase', + destination: '/docs', + }, ] From fb9c566e219a5454027d7f92517269c4dec9a8a6 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Wed, 29 Mar 2023 13:09:45 +0200 Subject: [PATCH 32/47] documenting the self-hosted --- .../pages/guides/getting-started/features.mdx | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/apps/docs/pages/guides/getting-started/features.mdx b/apps/docs/pages/guides/getting-started/features.mdx index 4cf63f342c0..f69a2f41901 100755 --- a/apps/docs/pages/guides/getting-started/features.mdx +++ b/apps/docs/pages/guides/getting-started/features.mdx @@ -149,35 +149,38 @@ Manage your projects programmatically. [Docs](/docs/reference/api). Both Postgres and the Supabase Platform are production-ready. Some tools we offer on top of Postgres are still under development. -| Product | Feature | Stage | -| -------------------------- | ---------------------- | ------- | -| Database | Postgres | `GA` | -| Database | Triggers | `GA` | -| Database | Functions | `GA` | -| Database | Extensions | `GA` | -| Database | Full Text Search | `GA` | -| Database | Webhooks | `alpha` | -| Database | Point-in-Time Recovery | `alpha` | -| Database | Vault | `alpha` | -| Studio | | `GA` | -| Realtime | Postgres Changes | `GA` | -| Realtime | Broadcast | `beta` | -| Realtime | Presence | `beta` | -| Storage | | `GA` | -| Storage | S3 Backend | `GA` | -| Storage | CDN | `GA` | -| Storage | Smart CDN | `beta` | -| Storage | Image Transformations | `beta` | -| Edge Functions | | `beta` | -| Auth | OAuth Providers | `beta` | -| Auth | Passwordless | `beta` | -| Auth | Next.js Auth Helpers | `alpha` | -| Auth | SvelteKit Auth Helpers | `alpha` | -| Auth | Remix Auth Helpers | `alpha` | -| Management API | | `beta` | -| CLI | | `beta` | -| Client Library: JavaScript | | `GA` | -| Client Library: Dart | | `beta` | +| Product | Feature | Stage | Available on self-hosted | +| -------------------------- | ---------------------- | ------- | ------------------------------------------- | +| Database | Postgres | `GA` | ✅ | +| Database | Triggers | `GA` | ✅ | +| Database | Functions | `GA` | ✅ | +| Database | Extensions | `GA` | ✅ | +| Database | Full Text Search | `GA` | ✅ | +| Database | Webhooks | `GA` | ✅ | +| Database | Point-in-Time Recovery | `alpha` | 🚧 [wal-g](https://github.com/wal-g/wal-g) | +| Database | Vault | `alpha` | ✅ | +| Studio | | `GA` | ✅ | +| Realtime | Postgres Changes | `GA` | ✅ | +| Realtime | Broadcast | `beta` | ✅ | +| Realtime | Presence | `beta` | ✅ | +| Storage | | `GA` | ✅ | +| Storage | S3 Backend | `GA` | ✅ | +| Storage | CDN | `GA` | 🚧 [Cloudflare](https://www.cloudflare.com) | +| Storage | Smart CDN | `beta` | 🚧 [Cloudflare](https://www.cloudflare.com) | +| Storage | Image Transformations | `beta` | ✅ | +| Edge Functions | | `beta` | 🚧 [Deno Deploy](https://deno.com/deploy) | +| Auth | OAuth Providers | `beta` | ✅ | +| Auth | Passwordless | `beta` | ✅ | +| Auth | Next.js Auth Helpers | `alpha` | ✅ | +| Auth | SvelteKit Auth Helpers | `alpha` | ✅ | +| Auth | Remix Auth Helpers | `alpha` | ✅ | +| Management API | | `beta` | N/A | +| CLI | | `beta` | N/A | +| Client Library: JavaScript | | `GA` | N/A | +| Client Library: Dart | | `beta` | N/A | + +- ✅ = Fully Available +- 🚧 = Available, but requires external tools or configuration export const Page = ({ children }) => From 6ce121e714aabdf45dd22b5a46b0845ee3d25267 Mon Sep 17 00:00:00 2001 From: Ollie Halliday <22655069+Hallidayo@users.noreply.github.com> Date: Wed, 29 Mar 2023 21:15:07 +0100 Subject: [PATCH 33/47] changed banner colouring --- studio/components/grid/components/header/RLSBannerWarning.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/studio/components/grid/components/header/RLSBannerWarning.tsx b/studio/components/grid/components/header/RLSBannerWarning.tsx index 9d5e16f422f..ca3954d62e0 100644 --- a/studio/components/grid/components/header/RLSBannerWarning.tsx +++ b/studio/components/grid/components/header/RLSBannerWarning.tsx @@ -29,7 +29,7 @@ export default function RLSBannerWarning() { <> {!isAcknowledged && !rlsEnabled && isPublicTable ? (
-
+
Warning: This table is publicly readable and writable.{' '} @@ -41,7 +41,7 @@ export default function RLSBannerWarning() {