)
})
-
-const OwnerDropdown = observer(({ members, member }: any) => {
- const PageState: any = useContext(PageContext)
- const { ui } = useStore()
- const { mutateOrgMembers } = useOrganizationDetail(ui.selectedOrganization?.slug || '')
- const [isOpen, setIsOpen] = useState(false)
- const [loading, setLoading] = useState(false)
-
- // handle modal visibility
- const [ownerTransferIsVisble, setOwnerTransferIsVisble] = useState(false)
-
- const { id: orgId, slug: orgSlug, stripe_customer_id, name: orgName } = PageState.organization
-
- async function handleMemberDelete() {
- setIsOpen(false)
- await timeout(200)
-
- confirmAlert({
- title: 'Confirm to remove',
- message: `This is permanent! Are you sure you want to remove ${member.profile.primary_email}?`,
- onAsyncConfirm: async () => {
- setLoading(true)
- const response = await delete_(`${API_URL}/organizations/${orgSlug}/members/remove`, {
- member_id: member.id,
- })
- if (response.error) {
- ui.setNotification({
- category: 'error',
- message: `Failed to delete user: ${response.error.message}`,
- })
- setLoading(false)
- } else {
- const updatedMembers = members.filter((x: any) => x.id !== member.id)
- mutateOrgMembers(updatedMembers)
- ui.setNotification({ category: 'success', message: 'Successfully removed member' })
- }
- },
- })
- }
-
- async function handleTransfer() {
- setLoading(true)
-
- const response = await post(`${API_URL}/organizations/${orgSlug}/transfer`, {
- org_id: orgId,
- member_id: member.id,
- })
- if (response.error) {
- ui.setNotification({
- category: 'error',
- message: `Failed to transfer ownership: ${response.error.message}`,
- })
- setLoading(false)
- } else {
- const updatedMembers = [...members]
- const oldOwner = updatedMembers.find((x) => x.is_owner == true)
- if (oldOwner) oldOwner.is_owner = false
- const newOwner = updatedMembers.find((x) => x.id == member.id)
- if (newOwner) newOwner.is_owner = true
- mutateOrgMembers(updatedMembers)
- setOwnerTransferIsVisble(false)
- ui.setNotification({ category: 'success', message: 'Successfully transfered organization' })
- }
- }
-
- async function handleResendInvite(member: Member) {
- setLoading(true)
-
- const response = await post(`${API_URL}/organizations/${orgSlug}/members/invite`, {
- invited_email: member.profile.primary_email,
- owner_id: member.invited_id,
- })
-
- if (response.error) {
- ui.setNotification({
- category: 'error',
- message: `Failed to resend invitation: ${response.error.message}`,
- })
- setLoading(false)
- } else {
- const updatedMembers = [...members]
- mutateOrgMembers(updatedMembers)
- ui.setNotification({ category: 'success', message: 'Resent the invitation.' })
- setLoading(false)
- }
- }
-
- async function handleRevokeInvitation(id: number) {
- setLoading(true)
-
- const response = await delete_(
- `${API_URL}/organizations/${orgSlug}/members/invite?invited_id=${id}`,
- {}
- )
-
- if (response.error) {
- ui.setNotification({
- category: 'error',
- message: `Failed to revoke invitation: ${response.error.message}`,
- })
- setLoading(false)
- } else {
- const updatedMembers = [...members]
- mutateOrgMembers(updatedMembers)
- ui.setNotification({ category: 'success', message: 'Successfully revoked the invitation.' })
- }
- }
-
- return (
-
- )
-})
diff --git a/studio/pages/project/[ref]/editor/[id].tsx b/studio/pages/project/[ref]/editor/[id].tsx
index b0aadab9925..77d1b2591bb 100644
--- a/studio/pages/project/[ref]/editor/[id].tsx
+++ b/studio/pages/project/[ref]/editor/[id].tsx
@@ -36,7 +36,9 @@ const TableEditorPage: NextPage = () => {
const selectedTable = !isNaN(Number(id))
? // @ts-ignore
tables.find((table) => table.id === Number(id))
- : tryParseJson(Base64.decode(id))
+ : id !== undefined
+ ? tryParseJson(Base64.decode(id))
+ : undefined
useEffect(() => {
if (selectedTable && 'schema' in selectedTable) {
diff --git a/studio/pages/project/[ref]/settings/api.tsx b/studio/pages/project/[ref]/settings/api.tsx
index 1a04c006b55..02039f27d6e 100644
--- a/studio/pages/project/[ref]/settings/api.tsx
+++ b/studio/pages/project/[ref]/settings/api.tsx
@@ -545,14 +545,10 @@ const PostgrestConfig = observer(({ config, projectRef }: any) => {
}
emptyMessage={
<>
-
-
- New schema you create will appear here
-
+
+
+
No schema available to choose
+
New schemas you create will appear here
>
}
diff --git a/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx b/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx
index 28f801d5e56..0dd9964fcd4 100644
--- a/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx
+++ b/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx
@@ -86,6 +86,7 @@ const PROVIDER_PHONE = {
SMS_TWILIO_AUTH_TOKEN: {
type: 'string',
title: 'Twilio Auth Token',
+ isSecret: true,
show: {
key: 'SMS_PROVIDER',
matches: 'twilio',
@@ -311,6 +312,7 @@ Client identifier when authenticating or validating users.
The secret key is a JWT token that must be generated.
[Learn more](https://supabase.com/docs/guides/auth/auth-apple#generate-a-client_secret)`,
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -356,6 +358,7 @@ const EXTERNAL_PROVIDER_AZURE = {
// [TODO] Change docs
title: 'Secret ID',
type: 'string',
+ isSecret: true,
},
EXTERNAL_AZURE_URL: {
// [TODO] Change docs
@@ -400,6 +403,7 @@ const EXTERNAL_PROVIDER_BITBUCKET = {
EXTERNAL_BITBUCKET_SECRET: {
title: 'Secret',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -437,6 +441,7 @@ const EXTERNAL_PROVIDER_DISCORD = {
EXTERNAL_DISCORD_SECRET: {
title: 'Client Secret',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -474,6 +479,7 @@ const EXTERNAL_PROVIDER_FACEBOOK = {
EXTERNAL_FACEBOOK_SECRET: {
title: 'Facebook secret',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -511,6 +517,7 @@ const EXTERNAL_PROVIDER_GITHUB = {
EXTERNAL_GITHUB_SECRET: {
title: 'Client Secret',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -550,6 +557,7 @@ const EXTERNAL_PROVIDER_GITLAB = {
EXTERNAL_GITLAB_SECRET: {
title: 'Secret',
type: 'string',
+ isSecret: true,
},
EXTERNAL_GITLAB_URL: {
title: 'Self Hosted GitLab URL',
@@ -595,6 +603,7 @@ const EXTERNAL_PROVIDER_GOOGLE = {
EXTERNAL_GOOGLE_SECRET: {
title: 'Client Secret',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -633,6 +642,7 @@ const EXTERNAL_PROVIDER_KEYCLOAK = {
EXTERNAL_KEYCLOAK_SECRET: {
title: 'Secret',
type: 'string',
+ isSecret: true,
},
EXTERNAL_KEYCLOAK_URL: {
title: 'Realm URL',
@@ -683,6 +693,7 @@ const EXTERNAL_PROVIDER_LINKEDIN = {
EXTERNAL_LINKEDIN_SECRET: {
title: 'API Secret Key',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -720,6 +731,7 @@ const EXTERNAL_PROVIDER_NOTION = {
EXTERNAL_NOTION_SECRET: {
title: 'OAuth client secret',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -757,6 +769,7 @@ const EXTERNAL_PROVIDER_TWITCH = {
EXTERNAL_TWITCH_SECRET: {
title: 'Client secret',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -794,6 +807,7 @@ const EXTERNAL_PROVIDER_TWITTER = {
EXTERNAL_TWITTER_SECRET: {
title: 'API Secret Key',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -831,6 +845,7 @@ const EXTERNAL_PROVIDER_SLACK = {
EXTERNAL_SLACK_SECRET: {
title: 'Client Secret',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -868,6 +883,7 @@ const EXTERNAL_PROVIDER_SPOTIFY = {
EXTERNAL_SPOTIFY_SECRET: {
title: 'Client Secret',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -909,6 +925,7 @@ const EXTERNAL_PROVIDER_WORKOS = {
EXTERNAL_WORKOS_SECRET: {
title: 'Secret Key',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
@@ -953,6 +970,7 @@ const EXTERNAL_PROVIDER_ZOOM = {
EXTERNAL_ZOOM_SECRET: {
title: 'Client secret',
type: 'string',
+ isSecret: true,
},
},
validationSchema: object().shape({
diff --git a/studio/tests/components/Auth/Auth.constants.test.js b/studio/tests/components/Auth/Auth.constants.test.js
index e10824d0d1e..842200dd2ef 100644
--- a/studio/tests/components/Auth/Auth.constants.test.js
+++ b/studio/tests/components/Auth/Auth.constants.test.js
@@ -45,6 +45,18 @@ describe('Auth.constants: domainRegex', () => {
const mockInput2 = 'https://supabase.com/*'
const output2 = domainRegex.test(mockInput2)
expect(output2).toBe(true)
+
+ const mockInput3 = 'https://new-*-domain.com/*'
+ const output3 = domainRegex.test(mockInput3)
+ expect(output3).toBe(true)
+
+ const mockInput4 = 'https://new-*-domain.com/*/*/*'
+ const output4 = domainRegex.test(mockInput4)
+ expect(output4).toBe(true)
+
+ const mockInput5 = 'https://sub-*-domain.new-*-domain.com/*/*/*'
+ const output5 = domainRegex.test(mockInput5)
+ expect(output5).toBe(true)
})
test('should invalidate invalid URLs', () => {
const mockInput1 = 'supabase'