mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
Update /project/_/settings/api page for consistency with other Project Settings pages (#37996)
* refactor api-settings page * Minor fix * nit spacing * Improve loading * Improve loading --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
1 parent
7144d88cdd
commit
803890f4f9
3 files changed
+346
-359
No files matched your search
@@ -11,12 +11,6 @@ import { z } from 'zod'
|
||||
import { useParams } from 'common'
|
||||
import { DocsButton } from 'components/ui/DocsButton'
|
||||
import { FormActions } from 'components/ui/Forms/FormActions'
|
||||
import {
|
||||
FormPanelContainer,
|
||||
FormPanelContent,
|
||||
FormPanelFooter,
|
||||
FormPanelHeader,
|
||||
} from 'components/ui/Forms/FormPanel'
|
||||
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
|
||||
import { useProjectPostgrestConfigUpdateMutation } from 'data/config/project-postgrest-config-update-mutation'
|
||||
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
|
||||
@@ -28,6 +22,10 @@ import {
|
||||
AlertTitle_Shadcn_,
|
||||
Alert_Shadcn_,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CollapsibleContent_Shadcn_,
|
||||
Collapsible_Shadcn_,
|
||||
FormControl_Shadcn_,
|
||||
@@ -39,7 +37,9 @@ import {
|
||||
Skeleton,
|
||||
Switch,
|
||||
WarningIcon,
|
||||
cn,
|
||||
} from 'ui'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
import {
|
||||
@@ -83,7 +83,7 @@ export const PostgrestConfig = () => {
|
||||
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
|
||||
const { data: config, isError } = useProjectPostgrestConfigQuery({ projectRef })
|
||||
const { data: config, isError, isLoading } = useProjectPostgrestConfigQuery({ projectRef })
|
||||
const { data: extensions } = useDatabaseExtensionsQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
@@ -101,10 +101,8 @@ export const PostgrestConfig = () => {
|
||||
|
||||
const formId = 'project-postgres-config'
|
||||
const hiddenSchema = ['auth', 'pgbouncer', 'hooks', 'extensions']
|
||||
const { can: canUpdatePostgrestConfig } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
'custom_config_postgrest'
|
||||
)
|
||||
const { can: canUpdatePostgrestConfig, isSuccess: isPermissionsLoaded } =
|
||||
useAsyncCheckProjectPermissions(PermissionAction.UPDATE, 'custom_config_postgrest')
|
||||
|
||||
const isGraphqlExtensionEnabled =
|
||||
(extensions ?? []).find((ext) => ext.name === 'pg_graphql')?.installed_version !== null
|
||||
@@ -168,275 +166,274 @@ export const PostgrestConfig = () => {
|
||||
const isDataApiEnabledInForm = form.getValues('enableDataApi')
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form_Shadcn_ {...form}>
|
||||
<form id={formId} onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<FormPanelContainer>
|
||||
<FormPanelHeader className="flex items-center justify-between">
|
||||
<span>Data API Settings</span>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<DocsButton href="https://supabase.com/docs/guides/database/connecting-to-postgres#data-apis" />
|
||||
<Button type="default" icon={<Lock />} onClick={() => setShowModal(true)}>
|
||||
Harden Data API
|
||||
</Button>
|
||||
</div>
|
||||
</FormPanelHeader>
|
||||
<FormPanelContent>
|
||||
{isError ? (
|
||||
<Admonition type="destructive" title="Failed to retrieve API settings" />
|
||||
) : (
|
||||
<>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="enableDataApi"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormItemLayout
|
||||
className="w-full px-8 py-8"
|
||||
layout="flex"
|
||||
label="Enable Data API"
|
||||
description="When enabled you will be able to use any Supabase client library and PostgREST endpoints with any schema configured below."
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
size="large"
|
||||
disabled={!canUpdatePostgrestConfig}
|
||||
checked={field.value}
|
||||
onCheckedChange={(value) => {
|
||||
field.onChange(value)
|
||||
if (!value) {
|
||||
form.setValue('enableDataApi', false)
|
||||
form.setValue('dbSchema', [])
|
||||
} else {
|
||||
form.setValue('enableDataApi', true)
|
||||
form.setValue('dbSchema', dbSchema)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
<Card id="postgrest-config">
|
||||
<CardHeader className="flex-row items-center justify-between">
|
||||
Data API Settings
|
||||
<div className="flex items-center gap-x-2">
|
||||
<DocsButton href="https://supabase.com/docs/guides/database/connecting-to-postgres#data-apis" />
|
||||
<Button type="default" icon={<Lock />} onClick={() => setShowModal(true)}>
|
||||
Harden Data API
|
||||
</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className={cn(!isLoading ? 'p-0' : '')}>
|
||||
<Form_Shadcn_ {...form}>
|
||||
<form id={formId} onSubmit={form.handleSubmit(onSubmit)}>
|
||||
{isLoading ? (
|
||||
<GenericSkeletonLoader />
|
||||
) : isError ? (
|
||||
<Admonition type="destructive" title="Failed to retrieve API settings" />
|
||||
) : (
|
||||
<>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="enableDataApi"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormItemLayout
|
||||
className="w-full px-8 py-8"
|
||||
layout="flex"
|
||||
label="Enable Data API"
|
||||
description="When enabled you will be able to use any Supabase client library and PostgREST endpoints with any schema configured below."
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Switch
|
||||
size="large"
|
||||
disabled={!canUpdatePostgrestConfig}
|
||||
checked={field.value}
|
||||
onCheckedChange={(value) => {
|
||||
field.onChange(value)
|
||||
if (!value) {
|
||||
form.setValue('enableDataApi', false)
|
||||
form.setValue('dbSchema', [])
|
||||
} else {
|
||||
form.setValue('enableDataApi', true)
|
||||
form.setValue('dbSchema', dbSchema)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
|
||||
{!field.value && (
|
||||
<>
|
||||
<Separator />
|
||||
<Alert_Shadcn_
|
||||
variant="warning"
|
||||
className="mb-0 border-none rounded-none"
|
||||
>
|
||||
<WarningIcon className="!left-[2rem]" />
|
||||
<AlertTitle_Shadcn_ className="!pl-[3.5rem] !left-[6rem]">
|
||||
No schemas can be queried
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_ className="!pl-[3.5rem]">
|
||||
<p>
|
||||
With this setting disabled, you will not be able to query any
|
||||
schemas via the Data API.
|
||||
</p>
|
||||
<p>
|
||||
You will see errors from the Postgrest endpoint
|
||||
<code className="text-xs">/rest/v1/</code>.
|
||||
</p>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
</>
|
||||
)}
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
<Collapsible_Shadcn_ open={form.getValues('enableDataApi')}>
|
||||
<CollapsibleContent_Shadcn_ className="border-t divide-y transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down">
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="dbSchema"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormItemLayout
|
||||
label="Exposed schemas"
|
||||
description="The schemas to expose in your API. Tables, views and stored procedures in
|
||||
these schemas will get API endpoints."
|
||||
layout="horizontal"
|
||||
className="px-8 py-8"
|
||||
>
|
||||
{isLoadingSchemas ? (
|
||||
<div className="col-span-12 flex flex-col gap-2 lg:col-span-7">
|
||||
<Skeleton className="w-full h-[38px]" />
|
||||
</div>
|
||||
) : (
|
||||
<MultiSelector
|
||||
onValuesChange={field.onChange}
|
||||
values={field.value}
|
||||
size="small"
|
||||
disabled={!canUpdatePostgrestConfig || !isDataApiEnabledInForm}
|
||||
>
|
||||
<MultiSelectorTrigger
|
||||
mode="inline-combobox"
|
||||
label="Select schemas for Data API..."
|
||||
badgeLimit="wrap"
|
||||
showIcon={false}
|
||||
deletableBadge
|
||||
/>
|
||||
<MultiSelectorContent>
|
||||
<MultiSelectorList>
|
||||
{schema.length <= 0 ? (
|
||||
<MultiSelectorItem key="empty" value="no">
|
||||
no
|
||||
</MultiSelectorItem>
|
||||
) : (
|
||||
<>
|
||||
{schema.map((x) => (
|
||||
<MultiSelectorItem
|
||||
key={x.id + '-' + x.name}
|
||||
value={x.name}
|
||||
>
|
||||
{x.name}
|
||||
</MultiSelectorItem>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</MultiSelectorList>
|
||||
</MultiSelectorContent>
|
||||
</MultiSelector>
|
||||
)}
|
||||
|
||||
{!field.value.includes('public') && field.value.length > 0 && (
|
||||
<Admonition
|
||||
type="default"
|
||||
title="The public schema for this project is not exposed"
|
||||
className="mt-2"
|
||||
description={
|
||||
<>
|
||||
<p>
|
||||
You will not be able to query tables and views in the{' '}
|
||||
<code>public</code> schema via supabase-js or HTTP clients.
|
||||
</p>
|
||||
{isGraphqlExtensionEnabled && (
|
||||
<>
|
||||
<p>
|
||||
Tables in the <code className="text-xs">public</code>{' '}
|
||||
schema are still exposed over our GraphQL endpoints.
|
||||
</p>
|
||||
<Button asChild type="default">
|
||||
<Link
|
||||
href={`/project/${projectRef}/database/extensions`}
|
||||
>
|
||||
Disable the pg_graphql extension
|
||||
</Link>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</FormItemLayout>
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="dbExtraSearchPath"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormItemLayout
|
||||
className="w-full px-8 py-8"
|
||||
layout="horizontal"
|
||||
label="Extra search path"
|
||||
description="Extra schemas to add to the search path of every request. Multiple schemas must be comma-separated."
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
size="small"
|
||||
disabled={!canUpdatePostgrestConfig || !isDataApiEnabledInForm}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="maxRows"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormItemLayout
|
||||
className="w-full px-8 py-8"
|
||||
layout="horizontal"
|
||||
label="Max rows"
|
||||
description="The maximum number of rows returned from a view, table, or stored procedure. Limits payload size for accidental or malicious requests."
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
size="small"
|
||||
disabled={!canUpdatePostgrestConfig || !isDataApiEnabledInForm}
|
||||
{...field}
|
||||
type="number"
|
||||
{...form.register('maxRows', {
|
||||
valueAsNumber: true, // Ensure the value is handled as a number
|
||||
})}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="dbPool"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormItemLayout
|
||||
className="w-full px-8 py-8"
|
||||
layout="horizontal"
|
||||
label="Pool size"
|
||||
description="Number of maximum connections to keep open in the Data API server's database pool. Unset to let it be configured automatically based on compute size."
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
size="small"
|
||||
disabled={!canUpdatePostgrestConfig || !isDataApiEnabledInForm}
|
||||
{...field}
|
||||
type="number"
|
||||
placeholder="Configured automatically based on compute size"
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value === '' ? null : Number(e.target.value)
|
||||
)
|
||||
}
|
||||
value={field.value === null ? '' : field.value}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
</CollapsibleContent_Shadcn_>
|
||||
</Collapsible_Shadcn_>
|
||||
</>
|
||||
)}
|
||||
<FormPanelFooter className="flex px-8 py-4">
|
||||
<FormActions
|
||||
form={formId}
|
||||
isSubmitting={isUpdating}
|
||||
hasChanges={form.formState.isDirty}
|
||||
handleReset={resetForm}
|
||||
disabled={!canUpdatePostgrestConfig}
|
||||
helper={
|
||||
!canUpdatePostgrestConfig
|
||||
? "You need additional permissions to update your project's API settings"
|
||||
: undefined
|
||||
}
|
||||
{!field.value && (
|
||||
<>
|
||||
<Separator />
|
||||
<Alert_Shadcn_
|
||||
variant="warning"
|
||||
className="mb-0 border-none rounded-none"
|
||||
>
|
||||
<WarningIcon className="!left-[2rem]" />
|
||||
<AlertTitle_Shadcn_ className="!pl-[3.5rem] !left-[6rem]">
|
||||
No schemas can be queried
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_ className="!pl-[3.5rem]">
|
||||
<p>
|
||||
With this setting disabled, you will not be able to query any
|
||||
schemas via the Data API.
|
||||
</p>
|
||||
<p>
|
||||
You will see errors from the Postgrest endpoint
|
||||
<code className="text-xs">/rest/v1/</code>.
|
||||
</p>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
</>
|
||||
)}
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
</FormPanelFooter>
|
||||
</FormPanelContent>
|
||||
</FormPanelContainer>
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
<Collapsible_Shadcn_ open={form.getValues('enableDataApi')}>
|
||||
<CollapsibleContent_Shadcn_ className="border-t divide-y transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down">
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="dbSchema"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormItemLayout
|
||||
label="Exposed schemas"
|
||||
description="The schemas to expose in your API. Tables, views and stored procedures in
|
||||
these schemas will get API endpoints."
|
||||
layout="horizontal"
|
||||
className="px-8 py-8"
|
||||
>
|
||||
{isLoadingSchemas ? (
|
||||
<div className="col-span-12 flex flex-col gap-2 lg:col-span-7">
|
||||
<Skeleton className="w-full h-[38px]" />
|
||||
</div>
|
||||
) : (
|
||||
<MultiSelector
|
||||
onValuesChange={field.onChange}
|
||||
values={field.value}
|
||||
size="small"
|
||||
disabled={!canUpdatePostgrestConfig || !isDataApiEnabledInForm}
|
||||
>
|
||||
<MultiSelectorTrigger
|
||||
mode="inline-combobox"
|
||||
label="Select schemas for Data API..."
|
||||
badgeLimit="wrap"
|
||||
showIcon={false}
|
||||
deletableBadge
|
||||
/>
|
||||
<MultiSelectorContent>
|
||||
<MultiSelectorList>
|
||||
{schema.length <= 0 ? (
|
||||
<MultiSelectorItem key="empty" value="no">
|
||||
no
|
||||
</MultiSelectorItem>
|
||||
) : (
|
||||
<>
|
||||
{schema.map((x) => (
|
||||
<MultiSelectorItem
|
||||
key={x.id + '-' + x.name}
|
||||
value={x.name}
|
||||
>
|
||||
{x.name}
|
||||
</MultiSelectorItem>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</MultiSelectorList>
|
||||
</MultiSelectorContent>
|
||||
</MultiSelector>
|
||||
)}
|
||||
|
||||
{!field.value.includes('public') && field.value.length > 0 && (
|
||||
<Admonition
|
||||
type="default"
|
||||
title="The public schema for this project is not exposed"
|
||||
className="mt-2"
|
||||
description={
|
||||
<>
|
||||
<p className="prose text-sm">
|
||||
You will not be able to query tables and views in the{' '}
|
||||
<code className="text-xs">public</code> schema via supabase-js
|
||||
or HTTP clients.
|
||||
</p>
|
||||
{isGraphqlExtensionEnabled && (
|
||||
<>
|
||||
<p className="prose text-sm mt-2">
|
||||
Tables in the <code className="text-xs">public</code>{' '}
|
||||
schema are still exposed over our GraphQL endpoints.
|
||||
</p>
|
||||
<Button asChild type="default" className="mt-2">
|
||||
<Link href={`/project/${projectRef}/database/extensions`}>
|
||||
Disable the pg_graphql extension
|
||||
</Link>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</FormItemLayout>
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="dbExtraSearchPath"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormItemLayout
|
||||
className="w-full px-8 py-8"
|
||||
layout="horizontal"
|
||||
label="Extra search path"
|
||||
description="Extra schemas to add to the search path of every request. Multiple schemas must be comma-separated."
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
size="small"
|
||||
disabled={!canUpdatePostgrestConfig || !isDataApiEnabledInForm}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="maxRows"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormItemLayout
|
||||
className="w-full px-8 py-8"
|
||||
layout="horizontal"
|
||||
label="Max rows"
|
||||
description="The maximum number of rows returned from a view, table, or stored procedure. Limits payload size for accidental or malicious requests."
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
size="small"
|
||||
disabled={!canUpdatePostgrestConfig || !isDataApiEnabledInForm}
|
||||
{...field}
|
||||
type="number"
|
||||
{...form.register('maxRows', {
|
||||
valueAsNumber: true, // Ensure the value is handled as a number
|
||||
})}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="dbPool"
|
||||
render={({ field }) => (
|
||||
<FormItem_Shadcn_ className="w-full">
|
||||
<FormItemLayout
|
||||
className="w-full px-8 py-8"
|
||||
layout="horizontal"
|
||||
label="Pool size"
|
||||
description="Number of maximum connections to keep open in the Data API server's database pool. Unset to let it be configured automatically based on compute size."
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
size="small"
|
||||
disabled={!canUpdatePostgrestConfig || !isDataApiEnabledInForm}
|
||||
{...field}
|
||||
type="number"
|
||||
placeholder="Configured automatically based on compute size"
|
||||
onChange={(e) =>
|
||||
field.onChange(
|
||||
e.target.value === '' ? null : Number(e.target.value)
|
||||
)
|
||||
}
|
||||
value={field.value === null ? '' : field.value}
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
</FormItem_Shadcn_>
|
||||
)}
|
||||
/>
|
||||
</CollapsibleContent_Shadcn_>
|
||||
</Collapsible_Shadcn_>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<FormActions
|
||||
form={formId}
|
||||
isSubmitting={isUpdating}
|
||||
hasChanges={form.formState.isDirty}
|
||||
handleReset={resetForm}
|
||||
disabled={!canUpdatePostgrestConfig}
|
||||
helper={
|
||||
isPermissionsLoaded && !canUpdatePostgrestConfig
|
||||
? "You need additional permissions to update your project's API settings"
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</CardFooter>
|
||||
|
||||
<HardenAPIModal visible={showModal} onClose={() => setShowModal(false)} />
|
||||
</>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { AlertCircle } from 'lucide-react'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { ScaffoldSection } from 'components/layouts/Scaffold'
|
||||
import DatabaseSelector from 'components/ui/DatabaseSelector'
|
||||
import Panel from 'components/ui/Panel'
|
||||
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
|
||||
import { useLoadBalancersQuery } from 'data/read-replicas/load-balancers-query'
|
||||
@@ -10,16 +10,22 @@ import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { PROJECT_STATUS } from 'lib/constants'
|
||||
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
|
||||
import { Badge, Input } from 'ui'
|
||||
import { Alert_Shadcn_, AlertTitle_Shadcn_, Badge, Card, CardContent, CardHeader } from 'ui'
|
||||
import { Input } from 'ui-patterns/DataInputs/Input'
|
||||
import { FormLayout } from 'ui-patterns/form/Layout/FormLayout'
|
||||
import { PostgrestConfig } from './PostgrestConfig'
|
||||
|
||||
const ServiceList = () => {
|
||||
export const ServiceList = () => {
|
||||
const { data: project, isLoading } = useSelectedProjectQuery()
|
||||
const { ref: projectRef } = useParams()
|
||||
const state = useDatabaseSelectorStateSnapshot()
|
||||
|
||||
const { data: customDomainData } = useCustomDomainsQuery({ projectRef })
|
||||
const { data: databases, isError } = useReadReplicasQuery({ projectRef })
|
||||
const {
|
||||
data: databases,
|
||||
isError,
|
||||
isLoading: isLoadingDatabases,
|
||||
} = useReadReplicasQuery({ projectRef })
|
||||
const { data: loadBalancers } = useLoadBalancersQuery({ projectRef })
|
||||
|
||||
// Get the API service
|
||||
@@ -36,77 +42,65 @@ const ServiceList = () => {
|
||||
: selectedDatabase?.restUrl
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isLoading ? (
|
||||
<GenericSkeletonLoader />
|
||||
) : project?.status !== PROJECT_STATUS.ACTIVE_HEALTHY ? (
|
||||
<div className="flex items-center justify-center rounded border border-overlay bg-surface-100 p-8">
|
||||
<ScaffoldSection isFullWidth id="api-settings" className="gap-6">
|
||||
{!isLoading && project?.status !== PROJECT_STATUS.ACTIVE_HEALTHY ? (
|
||||
<Alert_Shadcn_ variant="destructive">
|
||||
<AlertCircle size={16} />
|
||||
<p className="text-sm text-foreground-light ml-2">
|
||||
<AlertTitle_Shadcn_>
|
||||
API settings are unavailable as the project is not active
|
||||
</p>
|
||||
</div>
|
||||
</AlertTitle_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
) : (
|
||||
<>
|
||||
<section>
|
||||
<Panel
|
||||
title={
|
||||
<div className="w-full flex items-center justify-between">
|
||||
<h5 className="mb-0">Project URL</h5>
|
||||
<DatabaseSelector
|
||||
additionalOptions={
|
||||
(loadBalancers ?? []).length > 0
|
||||
? [{ id: 'load-balancer', name: 'API Load Balancer' }]
|
||||
: []
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Panel.Content>
|
||||
{isError ? (
|
||||
<div className="flex items-center justify-center py-4 space-x-2">
|
||||
<AlertCircle size={16} />
|
||||
<p className="text-sm text-foreground-light">Failed to retrieve project URL</p>
|
||||
</div>
|
||||
) : (
|
||||
<Input
|
||||
copy
|
||||
label={
|
||||
isCustomDomainActive ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<p>URL</p>
|
||||
<Badge>Custom domain active</Badge>
|
||||
</div>
|
||||
) : (
|
||||
'URL'
|
||||
)
|
||||
}
|
||||
readOnly
|
||||
disabled
|
||||
className="input-mono"
|
||||
value={endpoint}
|
||||
descriptionText={
|
||||
loadBalancerSelected
|
||||
? 'RESTful endpoint for querying and managing your databases through your load balancer'
|
||||
: replicaSelected
|
||||
? 'RESTful endpoint for querying your read replica'
|
||||
: 'RESTful endpoint for querying and managing your database'
|
||||
}
|
||||
layout="horizontal"
|
||||
/>
|
||||
)}
|
||||
</Panel.Content>
|
||||
</Panel>
|
||||
</section>
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between">
|
||||
Project URL
|
||||
<DatabaseSelector
|
||||
additionalOptions={
|
||||
(loadBalancers ?? []).length > 0
|
||||
? [{ id: 'load-balancer', name: 'API Load Balancer' }]
|
||||
: []
|
||||
}
|
||||
/>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isLoading || isLoadingDatabases ? (
|
||||
<GenericSkeletonLoader />
|
||||
) : isError ? (
|
||||
<Alert_Shadcn_ variant="destructive">
|
||||
<AlertCircle size={16} />
|
||||
<AlertTitle_Shadcn_>Failed to retrieve project URL</AlertTitle_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
) : (
|
||||
<FormLayout
|
||||
layout="horizontal"
|
||||
label={
|
||||
isCustomDomainActive ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<p>URL</p>
|
||||
<Badge>Custom domain active</Badge>
|
||||
</div>
|
||||
) : (
|
||||
'URL'
|
||||
)
|
||||
}
|
||||
description={
|
||||
loadBalancerSelected
|
||||
? 'RESTful endpoint for querying and managing your databases through your load balancer'
|
||||
: replicaSelected
|
||||
? 'RESTful endpoint for querying your read replica'
|
||||
: 'RESTful endpoint for querying and managing your database'
|
||||
}
|
||||
>
|
||||
<Input copy readOnly disabled className="input-mono" value={endpoint} />
|
||||
</FormLayout>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<section id="postgrest-config">
|
||||
<PostgrestConfig />
|
||||
</section>
|
||||
<PostgrestConfig />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</ScaffoldSection>
|
||||
)
|
||||
}
|
||||
|
||||
export default ServiceList
|
||||
@@ -1,27 +1,23 @@
|
||||
import ServiceList from 'components/interfaces/Settings/API/ServiceList'
|
||||
import { ServiceList } from 'components/interfaces/Settings/API/ServiceList'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import { PageLayout } from 'components/layouts/PageLayout/PageLayout'
|
||||
import SettingsLayout from 'components/layouts/ProjectSettingsLayout/SettingsLayout'
|
||||
import { ScaffoldContainer, ScaffoldHeader, ScaffoldTitle } from 'components/layouts/Scaffold'
|
||||
import { ScaffoldContainer } from 'components/layouts/Scaffold'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
|
||||
const ApiSettings: NextPageWithLayout = () => {
|
||||
return (
|
||||
<>
|
||||
<ScaffoldContainer id="billing-page-top">
|
||||
<ScaffoldHeader>
|
||||
<ScaffoldTitle>API Settings</ScaffoldTitle>
|
||||
</ScaffoldHeader>
|
||||
</ScaffoldContainer>
|
||||
<ScaffoldContainer bottomPadding>
|
||||
<ServiceList />
|
||||
</ScaffoldContainer>
|
||||
</>
|
||||
<ScaffoldContainer bottomPadding>
|
||||
<ServiceList />
|
||||
</ScaffoldContainer>
|
||||
)
|
||||
}
|
||||
|
||||
ApiSettings.getLayout = (page) => (
|
||||
<DefaultLayout>
|
||||
<SettingsLayout title="API Settings">{page}</SettingsLayout>
|
||||
<SettingsLayout title="API Settings">
|
||||
<PageLayout title="API Settings">{page}</PageLayout>
|
||||
</SettingsLayout>
|
||||
</DefaultLayout>
|
||||
)
|
||||
export default ApiSettings
|
||||
Reference in new issue
Block a user