Chore/update database settings (#20575)

* Add connection string syntax

* Add more description to pooling modes

* Fix

* Test

* Fix

* Sm wording change

* Update apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.tsx

* prettier

---------

Co-authored-by: Terry Sutton <saltcod@gmail.com>
Co-authored-by: Kevin Grüneberg <k.grueneberg1994@gmail.com>
This commit is contained in:
Joshen Lim
2024-01-22 11:07:17 +07:00
committed by GitHub
parent 7845bafaeb
commit 683fe3a6cd
10 changed files with 776 additions and 365 deletions

View File

@@ -1,5 +1,4 @@
import { zodResolver } from '@hookform/resolvers/zod'
import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { Fragment, useEffect } from 'react'
@@ -32,7 +31,6 @@ import { usePoolingConfigurationUpdateMutation } from 'data/database/pooling-con
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { useCheckPermissions, useStore } from 'hooks'
import { POOLING_OPTIMIZATIONS } from './ConnectionPooling.constants'
import { constructConnStringSyntax, getPoolerTld } from './ConnectionPooling.utils'
const formId = 'connection-pooling-form'
@@ -73,21 +71,9 @@ export const ConnectionPooling = () => {
isSuccess,
} = usePoolingConfigurationQuery({ projectRef: projectRef })
const poolerTld = isSuccess ? getPoolerTld(poolingInfo.connectionString) : 'com'
const connectionPoolingUnavailable =
!poolingInfo?.pgbouncer_enabled && poolingInfo?.pool_mode === null
const poolerConnStringSyntax = isSuccess
? constructConnStringSyntax(poolingInfo?.connectionString, {
ref: projectRef as string,
cloudProvider: projectIsLoading ? '' : project?.cloud_provider || '',
region: projectIsLoading ? '' : project?.region || '',
tld: poolerTld,
portNumber: poolingInfo.db_port.toString(),
})
: []
// [Joshen] TODO this needs to be obtained from BE as 26th Jan is when we'll start - projects will be affected at different rates
const resolvesToIpV6 = !poolingInfo?.supavisor_enabled && false // Number(new Date()) > Number(dayjs.utc('01-26-2024', 'MM-DD-YYYY').toDate())
@@ -166,21 +152,22 @@ export const ConnectionPooling = () => {
<p>
{connectionPoolingUnavailable
? 'Connection Pooling is not available for this project'
: 'Connect to your database via connection pooling'}
: 'Connection pooling configuration'}
</p>
{isSuccess && (
<div className="flex items-center gap-x-1">
<Badge color={poolingInfo?.supavisor_enabled ? 'green' : 'scale'}>
With {poolingInfo?.supavisor_enabled ? 'Supavisor' : 'PGBouncer'}
</Badge>
<Badge color={resolvesToIpV6 ? 'amber' : 'scale'}>
{resolvesToIpV6 ? 'Resolves to IPv6' : 'Resolves to IPv4'}
<Badge color="scale">
{poolingInfo?.supavisor_enabled ? 'Supavisor' : 'PGBouncer'}
</Badge>
</div>
)}
</div>
<Button asChild type="default" icon={<IconExternalLink strokeWidth={1.5} />}>
<a href="https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler">
<a
target="_blank"
rel="noreferrer"
href="https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler"
>
Documentation
</a>
</Button>
@@ -256,10 +243,20 @@ export const ConnectionPooling = () => {
label="Transaction"
value="transaction"
>
Transaction
<p>Transaction mode</p>
<p className="text-xs text-foreground-lighter">
Connection is assigned to the client for the duration of a
transaction. Some session-based Postgres features such as prepared
statements are not available with this option.
</p>
</Listbox.Option>
<Listbox.Option key="session" label="Session" value="session">
Session
<p>Session mode</p>
<p className="text-xs text-foreground-lighter">
When a new client connects, a connection is assigned to the client
until it disconnects. All Postgres features can be used with this
option.
</p>
</Listbox.Option>
</Listbox>
</FormControl_Shadcn_>
@@ -406,70 +403,6 @@ export const ConnectionPooling = () => {
<div className="border-muted border-t"></div>
</>
)}
<Input
className="input-mono w-full px-8 py-8 flex items-center"
layout="horizontal"
readOnly
copy
disabled
value={poolingInfo?.db_port}
label="Port Number"
/>
<div className="border-muted border-t"></div>
<Input
className="input-mono w-full px-8 py-8"
layout="vertical"
readOnly
copy
disabled
label="Connection string"
value={poolingInfo?.connectionString}
descriptionText={
poolingInfo.supavisor_enabled && (
<div className="flex flex-col gap-y-1">
<p className="text-sm">
You may also connect to another database or with another user via Supavisor
with the following URI format:
</p>
{poolerConnStringSyntax.length > 0 && (
<p className="text-sm font-mono tracking-tighter">
{poolerConnStringSyntax.map((x, idx) => {
if (x.tooltip) {
return (
<Tooltip.Root key={`syntax-${idx}`} delayDuration={0}>
<Tooltip.Trigger asChild>
<span className="text-foreground">{x.value}</span>
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'rounded bg-alternative py-1 px-2 leading-none shadow',
'border border-background',
].join(' ')}
>
<span className="text-xs text-foreground">{x.tooltip}</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Portal>
</Tooltip.Root>
)
} else {
return x.value
}
})}
</p>
)}
</div>
)
}
/>
</>
)}
</Panel>

View File

@@ -1,59 +0,0 @@
// [Joshen] This is to the best of interpreting the syntax from the API response
// // There's different format for PG13 (depending on authentication method being md5) and PG14
export const constructConnStringSyntax = (
connString: string,
{
ref,
cloudProvider,
region,
tld,
portNumber,
}: { ref: string; cloudProvider: string; region: string; tld: string; portNumber: string }
) => {
if (connString.includes('postgres:[YOUR-PASSWORD]')) {
// PG 13 + Authentication MD5
return [
{ value: 'postgres://', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
{ value: ':', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
{ value: '@', tooltip: undefined },
{ value: cloudProvider.toLocaleLowerCase(), tooltip: 'Cloud provider' },
{ value: '-0-', tooltip: undefined },
{ value: region, tooltip: "Project's region" },
{ value: `.pooler.supabase.${tld}:`, tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: '/', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
{ value: `?options=reference%3D`, tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
} else {
return [
{ value: 'postgres://', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
{ value: '.', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
{ value: ':', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
{ value: '@', tooltip: undefined },
{ value: cloudProvider.toLocaleLowerCase(), tooltip: 'Cloud provider' },
{ value: '-0-', tooltip: undefined },
{ value: region, tooltip: "Project's region" },
{ value: `.pooler.supabase.${tld}:`, tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: '/', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
]
}
}
export const getPoolerTld = (connString: string) => {
try {
const segment = connString.split('pooler.supabase.')[1]
const tld = segment.split(':6543')[0]
return tld
} catch {
return 'com'
}
}

View File

@@ -0,0 +1,85 @@
import { useParams } from 'common'
import Link from 'next/link'
import {
Alert_Shadcn_,
IconAlertTriangle,
AlertTitle_Shadcn_,
AlertDescription_Shadcn_,
Button,
IconExternalLink,
} from 'ui'
import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query'
import { useSelectedOrganization } from 'hooks'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useState } from 'react'
import ConfirmDisableReadOnlyModeModal from './DatabaseSettings/ConfirmDisableReadOnlyModal'
export const DatabaseReadOnlyAlert = () => {
const { ref: projectRef } = useParams()
const organization = useSelectedOrganization()
const [showConfirmationModal, setShowConfirmationModal] = useState(false)
const { data: resourceWarnings } = useResourceWarningsQuery()
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: organization?.slug })
const isReadOnlyMode =
(resourceWarnings ?? [])?.find((warning) => warning.project === projectRef)
?.is_readonly_mode_enabled ?? false
return (
<>
{isReadOnlyMode && (
<Alert_Shadcn_ variant="destructive">
<IconAlertTriangle />
<AlertTitle_Shadcn_>
Project is in read-only mode and database is no longer accepting write requests
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
You have reached 95% of your project's disk space, and read-only mode has been enabled
to preserve your database's stability and prevent your project from exceeding its
current billing plan. To resolve this, you may:
<ul className="list-disc pl-6 mt-1">
<li>
Temporarily disable read-only mode to free up space and reduce your database size
</li>
{subscription?.plan.id === 'free' ? (
<li>
<Link href={`/org/${organization?.slug}/billing?panel=subscriptionPlan`}>
<a className="text underline">Upgrade to the Pro plan</a>
</Link>{' '}
to increase your database size limit to 8GB.
</li>
) : subscription?.plan.id === 'pro' && subscription?.usage_billing_enabled ? (
<li>
<Link href={`/org/${organization?.slug}/billing?panel=subscriptionPlan`}>
<a className="text-foreground underline">Disable your Spend Cap</a>
</Link>{' '}
to allow your project to auto-scale and expand beyond the 8GB database size limit
</li>
) : null}
</ul>
</AlertDescription_Shadcn_>
<div className="mt-4 flex items-center space-x-2">
<Button type="default" onClick={() => setShowConfirmationModal(true)}>
Disable read-only mode
</Button>
<Button asChild type="default" icon={<IconExternalLink />}>
<a
href="https://supabase.com/docs/guides/platform/database-size#disabling-read-only-mode"
target="_blank"
rel="noreferrer"
>
Learn more
</a>
</Button>
</div>
</Alert_Shadcn_>
)}
<ConfirmDisableReadOnlyModeModal
visible={showConfirmationModal}
onClose={() => setShowConfirmationModal(false)}
/>
</>
)
}

View File

@@ -1,23 +1,32 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { useParams, useTelemetryProps } from 'common'
import { useRouter } from 'next/router'
import { useEffect, useRef, useState } from 'react'
import { Input, Separator, Tabs } from 'ui'
import { Button, IconExternalLink, Input, Separator, Tabs } from 'ui'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import AlertError from 'components/ui/AlertError'
import DatabaseSelector from 'components/ui/DatabaseSelector'
import Panel from 'components/ui/Panel'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query'
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
import { useFlag } from 'hooks'
import { pluckObjectFields } from 'lib/helpers'
import Telemetry from 'lib/telemetry'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import { getConnectionStrings } from './DatabaseSettings.utils'
import { IPv4DeprecationNotice } from '../IPv4DeprecationNotice'
import { UsePoolerCheckbox } from '../UsePoolerCheckbox'
import {
constructConnStringSyntax,
getConnectionStrings,
getPoolerTld,
} from './DatabaseSettings.utils'
const CONNECTION_TYPES = [
{ id: 'psql', label: 'PSQL' },
{ id: 'uri', label: 'URI' },
{ id: 'psql', label: 'PSQL' },
{ id: 'golang', label: 'Golang' },
{ id: 'jdbc', label: 'JDBC' },
{ id: 'dotnet', label: '.NET' },
@@ -28,16 +37,22 @@ const CONNECTION_TYPES = [
export const DatabaseConnectionString = () => {
const router = useRouter()
const { project: projectDetails } = useProjectContext()
const { project: projectDetails, isLoading: isProjectLoading } = useProjectContext()
const { ref: projectRef, connectionString } = useParams()
const telemetryProps = useTelemetryProps()
const readReplicasEnabled = useFlag('readReplicas') && projectDetails?.is_read_replicas_enabled
const state = useDatabaseSelectorStateSnapshot()
const readReplicasEnabled = useFlag('readReplicas') && projectDetails?.is_read_replicas_enabled
const connectionStringsRef = useRef<HTMLDivElement>(null)
const [usePoolerConnection, setUsePoolerConnection] = useState(true)
const [selectedTab, setSelectedTab] = useState<
'psql' | 'uri' | 'golang' | 'jdbc' | 'dotnet' | 'nodejs' | 'php' | 'python'
>('psql')
'uri' | 'psql' | 'golang' | 'jdbc' | 'dotnet' | 'nodejs' | 'php' | 'python'
>('uri')
const { data: poolingInfo, isSuccess: isSuccessPoolingInfo } = usePoolingConfigurationQuery({
projectRef,
})
const {
data,
@@ -70,6 +85,10 @@ export const DatabaseConnectionString = () => {
const connectionInfo = readReplicasEnabled
? pluckObjectFields(selectedDatabase || emptyState, DB_FIELDS)
: pluckObjectFields(project || emptyState, DB_FIELDS)
const connectionTld =
projectDetails?.restUrl !== undefined
? new URL(projectDetails?.restUrl ?? '').hostname.split('.').pop() ?? 'co'
: 'co'
const handleCopy = (id: string) => {
const labelValue = CONNECTION_TYPES.find((type) => type.id === id)?.label
@@ -84,7 +103,26 @@ export const DatabaseConnectionString = () => {
)
}
const connectionStrings = getConnectionStrings(connectionInfo)
const connectionStrings = isSuccessPoolingInfo
? getConnectionStrings(connectionInfo, poolingInfo, {
projectRef,
usePoolerConnection,
})
: { uri: '', psql: '', golang: '', jdbc: '', dotnet: '', nodejs: '', php: '', python: '' }
const poolerTld = isSuccessPoolingInfo ? getPoolerTld(poolingInfo.connectionString) : 'com'
const poolerConnStringSyntax = isSuccessPoolingInfo
? constructConnStringSyntax(poolingInfo.connectionString, {
selectedTab,
usePoolerConnection,
ref: projectRef as string,
cloudProvider: isProjectLoading ? '' : project?.cloud_provider || '',
region: isProjectLoading ? '' : project?.region || '',
tld: usePoolerConnection ? poolerTld : connectionTld,
portNumber: usePoolerConnection
? poolingInfo.db_port.toString()
: connectionInfo.db_port.toString(),
})
: []
useEffect(() => {
if (
@@ -99,40 +137,103 @@ export const DatabaseConnectionString = () => {
return (
<div id="connection-string">
<div ref={connectionStringsRef} className="flex flex-col py-4">
<div className="px-6 mb-2">
<h5 key="panel-title" className="mb-0">
Connection string
</h5>
{readReplicasEnabled && <DatabaseSelector />}
</div>
<Tabs
type="underlined"
size="tiny"
activeId={selectedTab}
baseClassNames="!space-y-0 -mb-[1px] px-6"
onChange={setSelectedTab}
>
{CONNECTION_TYPES.map((type) => (
<Tabs.Panel key={type.id} id={type.id} label={type.label} />
))}
</Tabs>
<Separator />
</div>
<div className="px-6 pb-4">
{isLoading && <ShimmeringLoader className="h-8 w-full" />}
{isError && <AlertError error={error} subject="Failed to retrieve database settings" />}
{isSuccess && (
<Input
copy
readOnly
disabled
value={connectionStrings[selectedTab]}
onCopy={() => handleCopy(selectedTab)}
/>
)}
</div>
<Panel
className="!m-0 [&>div:nth-child(1)]:!border-0 [&>div:nth-child(1)>div]:!p-0"
title={
<div ref={connectionStringsRef} className="w-full flex flex-col pt-4">
<div className="flex items-center justify-between px-6 mb-2">
<h5 key="panel-title" className="mb-0">
Connection string
</h5>
<div className="flex items-center gap-x-2">
{readReplicasEnabled && <DatabaseSelector />}
<Button asChild type="default" icon={<IconExternalLink strokeWidth={1.5} />}>
<a href="https://supabase.com/docs/guides/database/connecting-to-postgres">
Documentation
</a>
</Button>
</div>
</div>
<Tabs
type="underlined"
size="tiny"
activeId={selectedTab}
baseClassNames="!space-y-0 px-6 -mb-[1px]"
onChange={setSelectedTab}
>
{CONNECTION_TYPES.map((type) => (
<Tabs.Panel key={type.id} id={type.id} label={type.label} />
))}
</Tabs>
<Separator />
</div>
}
>
<Panel.Content>
{isLoading && <ShimmeringLoader className="h-8 w-full" />}
{isError && <AlertError error={error} subject="Failed to retrieve database settings" />}
{isSuccess && (
<div className="flex flex-col gap-y-4 pt-2">
<UsePoolerCheckbox
id="connection-string"
checked={usePoolerConnection}
onCheckedChange={setUsePoolerConnection}
/>
{!usePoolerConnection && <IPv4DeprecationNotice />}
<Input
copy
readOnly
disabled
className="input-mono [&>div>div>div>input]:text-xs [&>div>div>div>input]:opacity-100"
value={connectionStrings[selectedTab]}
onCopy={() => handleCopy(selectedTab)}
/>
{poolerConnStringSyntax.length > 0 && poolingInfo?.supavisor_enabled && (
<div className="flex flex-col gap-y-1 text-foreground-light">
<p className="text-sm">
You can use the following URI format to switch to a different database or user
when using connection pooling.
</p>
<p className="text-sm font-mono tracking-tight text-foreground-lighter">
{poolerConnStringSyntax.map((x, idx) => {
if (x.tooltip) {
return (
<Tooltip.Root key={`syntax-${idx}`} delayDuration={0}>
<Tooltip.Trigger asChild>
<span className="text-foreground text-xs">{x.value}</span>
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'rounded bg-alternative py-1 px-2 leading-none shadow',
'border border-background',
].join(' ')}
>
<span className="text-xs text-foreground">{x.tooltip}</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Portal>
</Tooltip.Root>
)
} else {
return (
<span key={`syntax-${idx}`} className="text-xs">
{x.value}
</span>
)
}
})}
</p>
</div>
)}
</div>
)}
</Panel.Content>
</Panel>
</div>
)
}

View File

@@ -1,5 +1,4 @@
import { useParams, useTelemetryProps } from 'common'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useRef, useState } from 'react'
@@ -9,9 +8,7 @@ import Panel from 'components/ui/Panel'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query'
import { useFlag, useSelectedOrganization, useSelectedProject } from 'hooks'
import { useFlag, useSelectedProject } from 'hooks'
import { pluckObjectFields } from 'lib/helpers'
import Telemetry from 'lib/telemetry'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
@@ -19,16 +16,14 @@ import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Badge,
Button,
IconAlertTriangle,
IconExternalLink,
IconAlertCircle,
Input,
Separator,
} from 'ui'
import ConfirmDisableReadOnlyModeModal from './ConfirmDisableReadOnlyModal'
import { IPv4DeprecationNotice } from '../IPv4DeprecationNotice'
import { UsePoolerCheckbox } from '../UsePoolerCheckbox'
import ResetDbPassword from './ResetDbPassword'
import { DatabaseConnectionString } from './DatabaseConnectionString'
import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query'
import { getHostFromConnectionString } from './DatabaseSettings.utils'
const DatabaseSettings = () => {
const router = useRouter()
@@ -36,17 +31,21 @@ const DatabaseSettings = () => {
const telemetryProps = useTelemetryProps()
const state = useDatabaseSelectorStateSnapshot()
const selectedProject = useSelectedProject()
const organization = useSelectedOrganization()
const readReplicasEnabled = useFlag('readReplicas')
const showReadReplicasUI = readReplicasEnabled && selectedProject?.is_read_replicas_enabled
const connectionStringsRef = useRef<HTMLDivElement>(null)
const [showConfirmationModal, setShowConfirmationModal] = useState(false)
const [usePoolerConnection, setUsePoolerConnection] = useState(true)
// [Joshen] TODO this needs to be obtained from BE as 26th Jan is when we'll start - projects will be affected at different rates
const resolvesToIpV6 = false // Number(new Date()) > Number(dayjs.utc('01-26-2024', 'MM-DD-YYYY').toDate())
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: organization?.slug })
const {
data: poolingInfo,
error: poolingInfoError,
isLoading: isLoadingPoolingInfo,
isError: isErrorPoolingInfo,
isSuccess: isSuccessPoolingInfo,
} = usePoolingConfigurationQuery({
projectRef,
})
const {
data,
error: projectSettingsError,
@@ -54,7 +53,6 @@ const DatabaseSettings = () => {
isError: isErrorProjectSettings,
isSuccess: isSuccessProjectSettings,
} = useProjectSettingsQuery({ projectRef })
const { data: resourceWarnings } = useResourceWarningsQuery()
const {
data: databases,
error: readReplicasError,
@@ -62,26 +60,40 @@ const DatabaseSettings = () => {
isError: isErrorReadReplicas,
isSuccess: isSuccessReadReplicas,
} = useReadReplicasQuery({ projectRef })
const error = showReadReplicasUI ? readReplicasError : projectSettingsError
const isLoading = showReadReplicasUI ? isLoadingReadReplicas : isLoadingProjectSettings
const isError = showReadReplicasUI ? isErrorReadReplicas : isErrorProjectSettings
const isSuccess = showReadReplicasUI ? isSuccessReadReplicas : isSuccessProjectSettings
const error = showReadReplicasUI ? readReplicasError : projectSettingsError || poolingInfoError
const isLoading = showReadReplicasUI
? isLoadingReadReplicas
: isLoadingProjectSettings || isLoadingPoolingInfo
const isError = showReadReplicasUI
? isErrorReadReplicas
: isErrorProjectSettings || isErrorPoolingInfo
const isSuccess = showReadReplicasUI
? isSuccessReadReplicas
: isSuccessProjectSettings || isSuccessPoolingInfo
const selectedDatabase = (databases ?? []).find(
(db) => db.identifier === state.selectedDatabaseId
)
const isReadOnlyMode =
(resourceWarnings ?? [])?.find((warning) => warning.project === projectRef)
?.is_readonly_mode_enabled ?? false
const isMd5 = poolingInfo?.connectionString.includes('?options=reference')
const { project } = data ?? {}
const DB_FIELDS = ['db_host', 'db_name', 'db_port', 'db_user', 'inserted_at']
const DB_FIELDS = ['db_host', 'db_name', 'db_port', 'db_user']
const emptyState = { db_user: '', db_host: '', db_port: '', db_name: '' }
const connectionInfo = showReadReplicasUI
const dbConnectionInfo = showReadReplicasUI
? pluckObjectFields(selectedDatabase || emptyState, DB_FIELDS)
: pluckObjectFields(project || emptyState, DB_FIELDS)
const connectionInfo = usePoolerConnection
? {
db_host: isSuccessPoolingInfo
? getHostFromConnectionString(poolingInfo?.connectionString)
: '',
db_name: poolingInfo?.db_name,
db_port: poolingInfo?.db_port,
db_user: `postgres.${projectRef}`,
}
: dbConnectionInfo
const handleCopy = (labelValue?: string) =>
Telemetry.sendEvent(
{
@@ -103,68 +115,16 @@ const DatabaseSettings = () => {
return (
<>
<section id="direct-connection">
{isReadOnlyMode && (
<Alert_Shadcn_ variant="destructive">
<IconAlertTriangle />
<AlertTitle_Shadcn_>
Project is in read-only mode and database is no longer accepting write requests
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
You have reached 95% of your project's disk space, and read-only mode has been enabled
to preserve your database's stability and prevent your project from exceeding its
current billing plan. To resolve this, you may:
<ul className="list-disc pl-6 mt-1">
<li>
Temporarily disable read-only mode to free up space and reduce your database size
</li>
{subscription?.plan.id === 'free' ? (
<li>
<Link href={`/org/${organization?.slug}/billing?panel=subscriptionPlan`}>
<a className="text underline">Upgrade to the Pro plan</a>
</Link>{' '}
to increase your database size limit to 8GB.
</li>
) : subscription?.plan.id === 'pro' && subscription?.usage_billing_enabled ? (
<li>
<Link href={`/org/${organization?.slug}/billing?panel=subscriptionPlan`}>
<a className="text-foreground underline">Disable your Spend Cap</a>
</Link>{' '}
to allow your project to auto-scale and expand beyond the 8GB database size
limit
</li>
) : null}
</ul>
</AlertDescription_Shadcn_>
<div className="mt-4 flex items-center space-x-2">
<Button type="default" onClick={() => setShowConfirmationModal(true)}>
Disable read-only mode
</Button>
<Button asChild type="default" icon={<IconExternalLink />}>
<Link
href="https://supabase.com/docs/guides/platform/database-size#disabling-read-only-mode"
target="_blank"
rel="noreferrer"
>
Learn more
</Link>
</Button>
</div>
</Alert_Shadcn_>
)}
<Panel
className="!m-0"
title={
<div className="w-full flex items-center justify-between">
<div className="flex items-center gap-x-2">
<h5 className="mb-0">Connect to your database directly</h5>
<Badge color={resolvesToIpV6 ? 'amber' : 'scale'}>
{resolvesToIpV6 ? 'Resolves to IPv6' : 'Resolves to IPv4'}
</Badge>
<h5 className="mb-0">Connection parameters</h5>
</div>
{showReadReplicasUI && <DatabaseSelector />}
</div>
}
className="!m-0"
>
<Panel.Content className="space-y-6">
{isLoading &&
@@ -180,51 +140,27 @@ const DatabaseSettings = () => {
{isError && <AlertError error={error} subject="Failed to retrieve databases" />}
{isSuccess && (
<>
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>
Direct database access via IPv4 and pgBouncer will be removed from January 26th
2024
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="space-y-3">
<p>
We strongly recommend using{' '}
<span
tabIndex={0}
className="cursor-pointer text-foreground underline underline-offset-[4px] decoration-brand-500 hover:decoration-foreground"
onClick={() => {
const connectionPooler = document.getElementById('connection-pooler')
connectionPooler?.scrollIntoView({ block: 'center', behavior: 'smooth' })
}}
>
connection pooling
</span>{' '}
to connect to your database. You'll only need to change the connection string
that you're using in your application to the pooler's connection string which
can be found in the{' '}
<span
tabIndex={0}
className="cursor-pointer text-foreground underline underline-offset-[4px] decoration-brand-500 hover:decoration-foreground"
onClick={() => {
const connectionPooler = document.getElementById('connection-pooler')
connectionPooler?.scrollIntoView({ block: 'center', behavior: 'smooth' })
}}
>
connection pooling settings
</span>
.
</p>
<Button asChild type="default" icon={<IconExternalLink strokeWidth={1.5} />}>
<a
href="https://github.com/orgs/supabase/discussions/17817"
target="_blank"
rel="noreferrer"
>
Learn more
</a>
</Button>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<div className="space-y-4">
<UsePoolerCheckbox
id="connection-params"
checked={usePoolerConnection}
onCheckedChange={setUsePoolerConnection}
/>
{!usePoolerConnection && <IPv4DeprecationNotice />}
{isMd5 && (
<Alert_Shadcn_>
<IconAlertCircle strokeWidth={2} />
<AlertTitle_Shadcn_>
If you are connecting to your database via a GUI client, use the{' '}
<span tabIndex={0}>connection string</span> above instead
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
GUI clients only support database connections for Postgres 13 via a
connection string.
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}
</div>
<Input
className="input-mono"
layout="horizontal"
@@ -237,7 +173,6 @@ const DatabaseSettings = () => {
handleCopy('Host')
}}
/>
<Input
className="input-mono"
layout="horizontal"
@@ -247,17 +182,26 @@ const DatabaseSettings = () => {
value={connectionInfo.db_name}
label="Database name"
/>
<Input
className="input-mono"
layout="horizontal"
readOnly
copy
disabled
value={connectionInfo.db_port.toString()}
value={connectionInfo.db_port}
label="Port"
/>
{isMd5 && (
<Input
className="input-mono"
layout="horizontal"
readOnly
copy
disabled
value={`reference=${projectRef}`}
label="Options"
/>
)}
<Input
layout="horizontal"
className="input-mono table-input-cell text-base"
@@ -267,7 +211,6 @@ const DatabaseSettings = () => {
value={connectionInfo.db_user}
label="User"
/>
<Input
className="input-mono"
layout="horizontal"
@@ -283,17 +226,10 @@ const DatabaseSettings = () => {
</>
)}
</Panel.Content>
<Separator />
<DatabaseConnectionString />
</Panel>
</section>
<ResetDbPassword disabled={isLoading || isError} />
<ConfirmDisableReadOnlyModeModal
visible={showConfirmationModal}
onClose={() => setShowConfirmationModal(false)}
/>
</>
)
}

View File

@@ -1,32 +1,50 @@
export const getConnectionStrings = (connectionInfo: {
db_user: string
db_port: number
db_host: string
db_name: string
}) => {
const uriConnString =
`postgresql://${connectionInfo.db_user}:[YOUR-PASSWORD]@` +
`${connectionInfo.db_host}:${connectionInfo.db_port.toString()}` +
`/${connectionInfo.db_name}`
import { PoolingConfiguration } from 'data/database/pooling-configuration-query'
export const getHostFromConnectionString = (str: string) => {
const segment = str.split('[YOUR-PASSWORD]@')
const [output] = segment[1].split(':')
return output
}
export const getConnectionStrings = (
connectionInfo: {
db_user: string
db_port: number
db_host: string
db_name: string
},
poolingInfo: PoolingConfiguration,
metadata: {
usePoolerConnection: boolean
projectRef?: string
pgVersion?: string
}
) => {
const { usePoolerConnection, projectRef } = metadata
// Pooler: user, host port
const user = usePoolerConnection ? `postgres.${projectRef}` : connectionInfo.db_user
const port = usePoolerConnection ? poolingInfo?.db_port : connectionInfo.db_port
// [Joshen] Temp FE: extract host from pooler connection string
const host = usePoolerConnection
? getHostFromConnectionString(poolingInfo.connectionString)
: connectionInfo.db_host
const name = usePoolerConnection ? poolingInfo?.db_name : connectionInfo.db_name
const uriConnString = usePoolerConnection
? poolingInfo?.connectionString
: `postgresql://${user}:[YOUR-PASSWORD]@` + `${host}:${port}` + `/${name}`
const golangConnString =
`user=${connectionInfo.db_user} password=[YOUR-PASSWORD] ` +
`host=${connectionInfo.db_host} port=${connectionInfo.db_port.toString()}` +
` dbname=${connectionInfo.db_name}`
const psqlConnString =
`psql -h ${connectionInfo.db_host} -p ` +
`${connectionInfo.db_port.toString()} -d ${connectionInfo.db_name} ` +
`-U ${connectionInfo.db_user}`
`user=${user} password=[YOUR-PASSWORD] ` + `host=${host} port=${port}` + ` dbname=${name}`
const psqlConnString = `psql -h ${host} -p ` + `${port} -d ${name} ` + `-U ${user}`
const jdbcConnString =
`jdbc:postgresql://${connectionInfo.db_host}:${connectionInfo.db_port.toString()}` +
`/${connectionInfo.db_name}?user=${connectionInfo.db_user}&password=[YOUR-PASSWORD]`
`jdbc:postgresql://${host}:${port}` + `/${name}?user=${user}&password=[YOUR-PASSWORD]`
const dotNetConnString =
`User Id=${connectionInfo.db_user};Password=[YOUR-PASSWORD];` +
`Server=${connectionInfo.db_host};Port=${connectionInfo.db_port.toString()};` +
`Database=${connectionInfo.db_name}`
`User Id=${user};Password=[YOUR-PASSWORD];` +
`Server=${host};Port=${port};` +
`Database=${name}`
const pythonConnString =
`user=${connectionInfo.db_user} password=[YOUR-PASSWORD]` +
` host=${connectionInfo.db_host} port=${connectionInfo.db_port.toString()}` +
` database=${connectionInfo.db_name}`
`user=${user} password=[YOUR-PASSWORD]` + ` host=${host} port=${port}` + ` database=${name}`
return {
psql: psqlConnString,
@@ -39,3 +57,302 @@ export const getConnectionStrings = (connectionInfo: {
python: pythonConnString,
}
}
// [Joshen] This is to the best of interpreting the syntax from the API response
// // There's different format for PG13 (depending on authentication method being md5) and PG14
export const constructConnStringSyntax = (
connString: string,
{
selectedTab,
usePoolerConnection,
ref,
cloudProvider,
region,
tld,
portNumber,
}: {
selectedTab: 'uri' | 'psql' | 'golang' | 'jdbc' | 'dotnet' | 'nodejs' | 'php' | 'python'
usePoolerConnection: boolean
ref: string
cloudProvider: string
region: string
tld: string
portNumber: string
}
) => {
const isMd5 = connString.includes('?options=reference')
const poolerHostDetails = [
{ value: cloudProvider.toLocaleLowerCase(), tooltip: 'Cloud provider' },
{ value: '-0-', tooltip: undefined },
{ value: region, tooltip: "Project's region" },
{ value: `.pooler.supabase.${tld}`, tooltip: undefined },
]
const dbHostDetails = [
{ value: 'db.', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
{ value: `.supabase.${tld}`, tooltip: undefined },
]
if (selectedTab === 'uri' || selectedTab === 'nodejs') {
if (isMd5) {
return [
{ value: 'postgres://', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
{ value: ':', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
{ value: '@', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: ':', tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: '/', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
...(usePoolerConnection
? [
{ value: `?options=reference%3D`, tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
: []),
]
} else {
return [
{ value: 'postgres://', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
...(usePoolerConnection
? [
{ value: '.', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
: []),
{ value: ':', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
{ value: '@', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: ':', tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: '/', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
]
}
}
if (selectedTab === 'psql') {
if (isMd5) {
return [
{ value: 'psql "postgresql://', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
{ value: ':', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
{ value: '@', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: ':', tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: '/', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
...(usePoolerConnection
? [
{ value: '?options=reference%3D', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
: []),
]
} else {
return [
{ value: 'psql -h ', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: ' -p ', tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: ' -d ', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
{ value: ' -U ', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
...(usePoolerConnection
? [
{ value: '.', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
: []),
]
}
}
if (selectedTab === 'golang' || selectedTab === 'php') {
if (isMd5) {
return [
{ value: 'user=', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
{ value: ' password=', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
{ value: ' host=', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: ' port=', tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: ' dbname=', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
...(usePoolerConnection
? [
{ value: ' options=reference=', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
: []),
]
} else {
return [
{ value: 'user=', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
...(usePoolerConnection
? [
{ value: '.', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
: []),
{ value: ' password=', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
{ value: ' host=', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: ' port=', tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: ' dbname=', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
]
}
}
if (selectedTab === 'jdbc') {
if (isMd5) {
return [
{ value: 'jdbc:postgresql://', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: ':', tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: '/', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
{ value: '?user=', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
{ value: '&password=', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
...(usePoolerConnection
? [
{ value: '&options=reference%3D', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
: []),
]
} else {
return [
{ value: 'jdbc:postgresql://', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: `:`, tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: '/', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
{ value: '?user=', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
...(usePoolerConnection
? [
{ value: '.', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
: []),
{ value: '&password=', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
]
}
}
if (selectedTab === 'dotnet') {
if (isMd5) {
return [
{ value: 'User Id=', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
{ value: ';Password=', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
{ value: ';Server=', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: ';Port=', tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: ';Database=', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
...(usePoolerConnection
? [
{ value: ";Options='reference=", tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
{ value: "'", tooltip: undefined },
]
: []),
]
} else {
return [
{ value: 'User Id=', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
...(usePoolerConnection
? [
{ value: '.', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
: []),
{ value: ';Password=', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
{ value: ';Server=', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: ';Port=', tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: ';Database=', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
]
}
}
if ('python') {
if (isMd5) {
return [
{ value: 'user=', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
{ value: ' password=', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
{ value: ' host=', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: ' port=', tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: ' database=', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
...(usePoolerConnection
? [
{ value: ' options=reference=', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
: []),
]
} else {
return [
{ value: 'user=', tooltip: undefined },
{ value: '[user]', tooltip: 'Database user (e.g postgres)' },
...(usePoolerConnection
? [
{ value: '.', tooltip: undefined },
{ value: ref, tooltip: "Project's reference ID" },
]
: []),
{ value: ' password=', tooltip: undefined },
{ value: '[password]', tooltip: 'Database password' },
{ value: ' host=', tooltip: undefined },
...(usePoolerConnection ? poolerHostDetails : dbHostDetails),
{ value: ' port=', tooltip: undefined },
{ value: portNumber, tooltip: 'Port number (Use 5432 if using prepared statements)' },
{ value: ' database=', tooltip: undefined },
{ value: '[db-name]', tooltip: 'Database name (e.g postgres)' },
]
}
}
return []
}
export const getPoolerTld = (connString: string) => {
try {
const segment = connString.split('pooler.supabase.')[1]
const tld = segment.split(':6543')[0]
return tld
} catch {
return 'com'
}
}

View File

@@ -0,0 +1,35 @@
import {
Alert_Shadcn_,
IconAlertTriangle,
AlertTitle_Shadcn_,
AlertDescription_Shadcn_,
Button,
IconExternalLink,
} from 'ui'
export const IPv4DeprecationNotice = () => {
return (
<Alert_Shadcn_ variant="warning">
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>
Direct database access via IPv4 and pgBouncer will be removed from January 26th 2024
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="space-y-3">
<p>
We strongly recommend using <span className="text-foreground">connection pooling</span> to
connect to your database. You'll only need to change the connection string that you're
using in your application to the pooler's connection string.
</p>
<Button asChild type="default" icon={<IconExternalLink strokeWidth={1.5} />}>
<a
href="https://github.com/orgs/supabase/discussions/17817"
target="_blank"
rel="noreferrer"
>
Learn more
</a>
</Button>
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)
}

View File

@@ -0,0 +1,53 @@
import { useParams } from 'common'
import { Badge, Checkbox_Shadcn_ } from 'ui'
import { Markdown } from 'components/interfaces/Markdown'
import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query'
interface UsePoolerCheckboxInterface {
id: string
checked: boolean
onCheckedChange: (value: boolean) => void
}
export const UsePoolerCheckbox = ({ id, checked, onCheckedChange }: UsePoolerCheckboxInterface) => {
const { ref: projectRef } = useParams()
const { data, isSuccess } = usePoolingConfigurationQuery({ projectRef })
// [Joshen] TODO this needs to be obtained from BE as 26th Jan is when we'll start - projects will be affected at different rates
const resolvesToIpV6 = !data?.supavisor_enabled && false // Number(new Date()) > Number(dayjs.utc('01-26-2024', 'MM-DD-YYYY').toDate())
return (
<div className="flex gap-x-3">
<Checkbox_Shadcn_
id={`use-pooler-${id}`}
checked={checked}
onCheckedChange={() => onCheckedChange(!checked)}
/>
<div className="-mt-[2px] flex flex-col gap-y-1 w-full">
<label htmlFor={`use-pooler-${id}`} className="text-sm cursor-pointer">
Use connection pooling
{isSuccess && checked && data.supavisor_enabled && (
<Badge color="scale" className="ml-2">
Supavisor
</Badge>
)}
<Badge color="scale" className="ml-2">
{checked
? 'Resolves to IPv4'
: resolvesToIpV6
? 'Resolves to IPv6'
: 'Resolves to IPv4'}
</Badge>
</label>
<Markdown
extLinks
className="[&>p]:m-0 space-y-1 text-foreground-lighter max-w-full"
content={`
IPv4 and IPv6 connections will resolve while using connection pooling\n
A connection pooler is useful for managing a large number of temporary connections. [Learn more](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler)`}
/>
</div>
</div>
)
}

View File

@@ -2,11 +2,14 @@ import { useQuery, UseQueryOptions } from '@tanstack/react-query'
import { get } from 'data/fetchers'
import { ResponseError } from 'types'
import { databaseKeys } from './keys'
import { components } from 'data/api'
export type PoolingConfigurationVariables = {
projectRef?: string
}
export type PoolingConfiguration = components['schemas']['PgbouncerConfigResponse']
export async function getPoolingConfiguration(
{ projectRef }: PoolingConfigurationVariables,
signal?: AbortSignal

View File

@@ -1,29 +1,36 @@
import { observer } from 'mobx-react-lite'
import { NextPageWithLayout } from 'types'
import { SettingsLayout } from 'components/layouts'
import {
ConnectionPooling,
DatabaseSettings,
NetworkRestrictions,
} from 'components/interfaces/Settings/Database'
import { SettingsLayout } from 'components/layouts'
import { observer } from 'mobx-react-lite'
import { NextPageWithLayout } from 'types'
import SSLConfiguration from 'components/interfaces/Settings/Database/SSLConfiguration'
import DiskSizeConfiguration from 'components/interfaces/Settings/Database/DiskSizeConfiguration'
import BannedIPs from 'components/interfaces/Settings/Database/BannedIPs'
import { DatabaseConnectionString } from 'components/interfaces/Settings/Database/DatabaseSettings/DatabaseConnectionString'
import DiskSizeConfiguration from 'components/interfaces/Settings/Database/DiskSizeConfiguration'
import SSLConfiguration from 'components/interfaces/Settings/Database/SSLConfiguration'
import { DatabaseReadOnlyAlert } from 'components/interfaces/Settings/Database/DatabaseReadOnlyAlert'
const ProjectSettings: NextPageWithLayout = () => {
return (
<div className="1xl:px-28 mx-auto flex flex-col px-5 pt-6 pb-14 lg:px-16 xl:px-24 2xl:px-32">
<div className="content h-full w-full overflow-y-auto space-y-10">
<div className="content h-full w-full overflow-y-auto space-y-6">
<h3 className="text-foreground text-xl">Database Settings</h3>
<div className="flex flex-col gap-y-4 !mt-6">
<ConnectionPooling />
<DatabaseSettings />
<div className="space-y-10">
<div className="flex flex-col gap-y-4">
<DatabaseReadOnlyAlert />
<DatabaseConnectionString />
<DatabaseSettings />
<ConnectionPooling />
</div>
<SSLConfiguration />
<DiskSizeConfiguration />
<NetworkRestrictions />
<BannedIPs />
</div>
<SSLConfiguration />
<DiskSizeConfiguration />
<NetworkRestrictions />
<BannedIPs />
</div>
</div>
)