From e2e208cecccccbb2e2e9910c5e4f9505901b671a Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 16 Aug 2022 20:52:36 +0800 Subject: [PATCH] Update database extensions page to enforce enabling some extensions in their own schema --- .../Auth/RedirectDomains/RedirectDomains.tsx | 1 + .../Extensions/EnableExtensionModal.tsx | 142 ++++++++++++++++++ .../Database/Extensions/ExtensionCard.tsx | 102 +++++++------ .../Extensions/Extensions.constants.ts | 2 + .../Database/Extensions/Extensions.tsx | 6 +- .../Storage/CreateBucketModal.js | 2 +- 6 files changed, 206 insertions(+), 49 deletions(-) create mode 100644 studio/components/interfaces/Database/Extensions/EnableExtensionModal.tsx diff --git a/studio/components/interfaces/Auth/RedirectDomains/RedirectDomains.tsx b/studio/components/interfaces/Auth/RedirectDomains/RedirectDomains.tsx index 60f4d9d15f6..8c066765281 100644 --- a/studio/components/interfaces/Auth/RedirectDomains/RedirectDomains.tsx +++ b/studio/components/interfaces/Auth/RedirectDomains/RedirectDomains.tsx @@ -116,6 +116,7 @@ const RedirectDomains = () => { form="new-domain-form" htmlType="submit" size="medium" + disabled={isSubmitting} loading={isSubmitting} > Add domain diff --git a/studio/components/interfaces/Database/Extensions/EnableExtensionModal.tsx b/studio/components/interfaces/Database/Extensions/EnableExtensionModal.tsx new file mode 100644 index 00000000000..56a3b22abb9 --- /dev/null +++ b/studio/components/interfaces/Database/Extensions/EnableExtensionModal.tsx @@ -0,0 +1,142 @@ +import { FC } from 'react' +import { Button, Input, Form, Modal, Listbox, IconPlus, IconDatabase } from '@supabase/ui' +import { PostgresExtension, PostgresSchema } from '@supabase/postgres-meta' + +import { useStore } from 'hooks' + +interface Props { + visible: boolean + extension: PostgresExtension + onCancel: () => void +} + +const EnableExtensionModal: FC = ({ visible, extension, onCancel }) => { + const { ui, meta } = useStore() + const schemas = meta.schemas.list() + + const validate = (values: any) => { + const errors: any = {} + if (values.schema === 'custom' && !values.name) errors.name = 'Required field' + return errors + } + + const onSubmit = async (values: any, { setSubmitting }: any) => { + setSubmitting(true) + if (values.schema === 'custom') { + const { error } = await meta.query(`create schema if not exists ${values.name}`) + if (error) { + return ui.setNotification({ + error, + category: 'error', + message: `Failed to create schema: ${error.message}`, + }) + } + } + + const { error } = await meta.extensions.create({ + name: extension.name, + schema: values.schema === 'custom' ? values.name : values.schema, + version: extension.default_version, + cascade: true, + }) + if (error) { + ui.setNotification({ + error, + category: 'error', + message: `Failed to toggle ${extension.name.toUpperCase()}: ${error.message}`, + }) + } else { + ui.setNotification({ + category: 'success', + message: `${extension.name.toUpperCase()} is on.`, + }) + } + + setSubmitting(false) + onCancel() + } + + return ( + +
Confirm to enable
+ {extension.name} + + } + > +
+ {({ isSubmitting, values, handleReset }: any) => { + return ( +
+ + + } + > + Create a new schema "{extension.name}" + + + {/* @ts-ignore */} + {schemas.map((schema: PostgresSchema) => { + return ( + } + > + {schema.name} + + ) + })} + + + + {values.schema === 'custom' && ( + + + + )} + + + +
+ + +
+
+
+ ) + }} +
+
+ ) +} + +export default EnableExtensionModal diff --git a/studio/components/interfaces/Database/Extensions/ExtensionCard.tsx b/studio/components/interfaces/Database/Extensions/ExtensionCard.tsx index 4fe8eea083b..bedb9105d7d 100644 --- a/studio/components/interfaces/Database/Extensions/ExtensionCard.tsx +++ b/studio/components/interfaces/Database/Extensions/ExtensionCard.tsx @@ -4,6 +4,8 @@ import { Badge, IconLoader, Toggle } from '@supabase/ui' import { useStore } from 'hooks' import { confirmAlert } from 'components/to-be-cleaned/ModalsDeprecated/ConfirmModal' +import { EXTENSIONS_IN_OWN_SCHEMA } from './Extensions.constants' +import EnableExtensionModal from './EnableExtensionModal' interface Props { extension: any @@ -14,10 +16,15 @@ const ExtensionCard: FC = ({ extension }) => { const isOn = extension.installed_version !== null const [loading, setLoading] = useState(false) + const [showConfirmEnableModal, setShowConfirmEnableModal] = useState(false) async function enableExtension() { + if (EXTENSIONS_IN_OWN_SCHEMA.includes(extension.name)) { + return setShowConfirmEnableModal(true) + } + confirmAlert({ - title: 'Confirm to turn on', + title: 'Confirm to enable extension', message: `Are you sure you want to turn ON "${extension.name}" extension?`, onAsyncConfirm: async () => { try { @@ -50,7 +57,7 @@ const ExtensionCard: FC = ({ extension }) => { async function disableExtension() { confirmAlert({ - title: 'Confirm to turn off', + title: 'Confirm to disable extension', message: `Are you sure you want to turn OFF "${extension.name}" extension?`, onAsyncConfirm: async () => { try { @@ -80,57 +87,62 @@ const ExtensionCard: FC = ({ extension }) => { } return ( -
+ <>
-

- {extension.name} -

- {loading ? ( - - ) : ( - (isOn ? disableExtension() : enableExtension())} - /> - )} -
-
-
-

- {extension.comment} -

+
+

+ {extension.name} +

+ {loading ? ( + + ) : ( + (isOn ? disableExtension() : enableExtension())} + /> + )}
- {isOn && extension.schema && ( +

- - Schema: {`${extension.schema}`} - + {extension.comment}

- )} + {isOn && extension.schema && ( +
+
+ Schema: + {`${extension.schema}`} +
+
+ )} +
-
+ setShowConfirmEnableModal(false)} + /> + ) } diff --git a/studio/components/interfaces/Database/Extensions/Extensions.constants.ts b/studio/components/interfaces/Database/Extensions/Extensions.constants.ts index 0816f74ffca..70644025c5d 100644 --- a/studio/components/interfaces/Database/Extensions/Extensions.constants.ts +++ b/studio/components/interfaces/Database/Extensions/Extensions.constants.ts @@ -12,3 +12,5 @@ export const HIDDEN_EXTENSIONS = [ 'pg_visibility', 'pgstattuple', ] + +export const EXTENSIONS_IN_OWN_SCHEMA = ['postgis', 'pgsodium', 'vault'] diff --git a/studio/components/interfaces/Database/Extensions/Extensions.tsx b/studio/components/interfaces/Database/Extensions/Extensions.tsx index d48f8d01b51..5b8fdb73ee4 100644 --- a/studio/components/interfaces/Database/Extensions/Extensions.tsx +++ b/studio/components/interfaces/Database/Extensions/Extensions.tsx @@ -1,7 +1,7 @@ import { FC, useState } from 'react' import { observer } from 'mobx-react-lite' import { partition, isNull } from 'lodash' -import { Input, IconSearch, Typography } from '@supabase/ui' +import { Input, IconSearch } from '@supabase/ui' import { useStore } from 'hooks' import ExtensionCard from './ExtensionCard' @@ -45,7 +45,7 @@ const Extensions: FC = ({}) => {
{enabledExtensions.length > 0 && (
-

Enabled

+

Enabled extensions

{enabledExtensions.map((extension) => ( @@ -56,7 +56,7 @@ const Extensions: FC = ({}) => { {disabledExtensions.length > 0 && (
-

Extensions

+

Available extensions

{disabledExtensions.map((extension) => ( diff --git a/studio/components/to-be-cleaned/Storage/CreateBucketModal.js b/studio/components/to-be-cleaned/Storage/CreateBucketModal.js index 7349b91e260..6be3f667dd4 100644 --- a/studio/components/to-be-cleaned/Storage/CreateBucketModal.js +++ b/studio/components/to-be-cleaned/Storage/CreateBucketModal.js @@ -53,7 +53,7 @@ const CreateBucketModal = ({ -