mirror of
https://github.com/supabase/supabase.git
synced 2026-10-17 15:19:08 +03:00
feat: support for multiple pg versions (#29473)
* feat: support multiple pg versions --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
1 parent
1ef75abc02
commit
d255e15efd
15 files changed
+728
-217
No files matched your search
@@ -0,0 +1,102 @@
|
||||
import { useEffect } from 'react'
|
||||
import { ControllerRenderProps, UseFormReturn } from 'react-hook-form'
|
||||
|
||||
import { components } from 'api-types'
|
||||
import {
|
||||
ProjectCreationPostgresVersion,
|
||||
useProjectCreationPostgresVersionsQuery,
|
||||
} from 'data/config/project-creation-postgres-versions-query'
|
||||
import type { CloudProvider } from 'shared-data'
|
||||
import {
|
||||
Badge,
|
||||
SelectContent_Shadcn_,
|
||||
SelectGroup_Shadcn_,
|
||||
SelectItem_Shadcn_,
|
||||
SelectTrigger_Shadcn_,
|
||||
SelectValue_Shadcn_,
|
||||
Select_Shadcn_,
|
||||
} from 'ui'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
|
||||
type ReleaseChannel = components['schemas']['ReleaseChannel']
|
||||
type PostgresEngine = components['schemas']['PostgresEngine']
|
||||
|
||||
interface PostgresVersionDetails {
|
||||
postgresEngine: PostgresEngine | undefined
|
||||
releaseChannel: ReleaseChannel | undefined
|
||||
}
|
||||
|
||||
interface PostgresVersionSelectorProps {
|
||||
cloudProvider: CloudProvider
|
||||
dbRegion: string
|
||||
organizationSlug: string | undefined
|
||||
field: ControllerRenderProps<any, 'postgresVersionSelection'>
|
||||
form: UseFormReturn<any>
|
||||
}
|
||||
|
||||
const formatValue = ({ postgres_engine, release_channel }: ProjectCreationPostgresVersion) => {
|
||||
return `${postgres_engine}|${release_channel}`
|
||||
}
|
||||
|
||||
export const extractPostgresVersionDetails = (value: string): PostgresVersionDetails => {
|
||||
if (!value) {
|
||||
return { postgresEngine: undefined, releaseChannel: undefined }
|
||||
}
|
||||
|
||||
const [postgresEngine, releaseChannel] = value.split('|')
|
||||
return { postgresEngine, releaseChannel } as PostgresVersionDetails
|
||||
}
|
||||
|
||||
export const PostgresVersionSelector = ({
|
||||
cloudProvider,
|
||||
dbRegion,
|
||||
organizationSlug,
|
||||
field,
|
||||
form,
|
||||
}: PostgresVersionSelectorProps) => {
|
||||
const { data, isLoading: isLoadingProjectVersions } = useProjectCreationPostgresVersionsQuery({
|
||||
cloudProvider,
|
||||
dbRegion,
|
||||
organizationSlug,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const defaultValue = data?.available_versions?.[0]
|
||||
? formatValue(data.available_versions[0])
|
||||
: undefined
|
||||
form.setValue('postgresVersionSelection', defaultValue)
|
||||
}, [data, form])
|
||||
|
||||
return (
|
||||
<FormItemLayout layout="horizontal" label="Postgres Version">
|
||||
<Select_Shadcn_
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
disabled={isLoadingProjectVersions}
|
||||
>
|
||||
<SelectTrigger_Shadcn_>
|
||||
<SelectValue_Shadcn_ placeholder="Select a Postgres version for your project" />
|
||||
</SelectTrigger_Shadcn_>
|
||||
<SelectContent_Shadcn_>
|
||||
<SelectGroup_Shadcn_>
|
||||
{(data?.available_versions || [])?.map((value) => {
|
||||
const postgresVersion = value.version.split('supabase-postgres-')[1]
|
||||
return (
|
||||
<SelectItem_Shadcn_ key={formatValue(value)} value={formatValue(value)}>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-foreground">{postgresVersion}</span>
|
||||
{value.release_channel !== 'ga' && (
|
||||
<Badge variant="warning" className="mr-1 capitalize">
|
||||
{value.release_channel}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem_Shadcn_>
|
||||
)
|
||||
})}
|
||||
</SelectGroup_Shadcn_>
|
||||
</SelectContent_Shadcn_>
|
||||
</Select_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)
|
||||
}
|
||||
+206
-143
@@ -1,28 +1,62 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { AlertCircle, AlertTriangle } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { toast } from 'sonner'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { useProjectUpgradeEligibilityQuery } from 'data/config/project-upgrade-eligibility-query'
|
||||
import { useProjectUpgradeMutation } from 'data/projects/project-upgrade-mutation'
|
||||
import {
|
||||
ProjectUpgradeTargetVersion,
|
||||
useProjectUpgradeEligibilityQuery,
|
||||
} from 'data/config/project-upgrade-eligibility-query'
|
||||
import { ReleaseChannel, useProjectUpgradeMutation } from 'data/projects/project-upgrade-mutation'
|
||||
import { setProjectStatus } from 'data/projects/projects-query'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { PROJECT_STATUS } from 'lib/constants'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
import {
|
||||
AlertDescription_Shadcn_,
|
||||
AlertTitle_Shadcn_,
|
||||
Alert_Shadcn_,
|
||||
Badge,
|
||||
Button,
|
||||
Form,
|
||||
Listbox,
|
||||
FormControl_Shadcn_,
|
||||
FormField_Shadcn_,
|
||||
Form_Shadcn_,
|
||||
Modal,
|
||||
SelectContent_Shadcn_,
|
||||
SelectGroup_Shadcn_,
|
||||
SelectItem_Shadcn_,
|
||||
SelectTrigger_Shadcn_,
|
||||
SelectValue_Shadcn_,
|
||||
Select_Shadcn_,
|
||||
TooltipContent_Shadcn_,
|
||||
TooltipTrigger_Shadcn_,
|
||||
Tooltip_Shadcn_,
|
||||
} from 'ui'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
|
||||
interface PostgresVersionDetails {
|
||||
postgresEngine: string
|
||||
releaseChannel: ReleaseChannel
|
||||
}
|
||||
|
||||
const formatValue = ({ postgres_version, release_channel }: ProjectUpgradeTargetVersion) => {
|
||||
return `${postgres_version}|${release_channel}`
|
||||
}
|
||||
|
||||
export const extractPostgresVersionDetails = (value: string): PostgresVersionDetails | null => {
|
||||
if (!value) {
|
||||
return null
|
||||
}
|
||||
|
||||
const [postgresEngine, releaseChannel] = value.split('|')
|
||||
return { postgresEngine, releaseChannel } as PostgresVersionDetails
|
||||
}
|
||||
|
||||
const ProjectUpgradeAlert = () => {
|
||||
const router = useRouter()
|
||||
@@ -32,7 +66,6 @@ const ProjectUpgradeAlert = () => {
|
||||
|
||||
const projectUpgradeDisabled = useFlag('disableProjectUpgrade')
|
||||
|
||||
const formId = 'project-upgrade-form'
|
||||
const { data } = useProjectUpgradeEligibilityQuery({ projectRef: ref })
|
||||
const currentPgVersion = (data?.current_app_version ?? '').split('supabase-postgres-')[1]
|
||||
const latestPgVersion = (data?.latest_app_version ?? '').split('supabase-postgres-')[1]
|
||||
@@ -40,20 +73,48 @@ const ProjectUpgradeAlert = () => {
|
||||
const durationEstimateHours = data?.duration_estimate_hours || 1
|
||||
const legacyAuthCustomRoles = data?.legacy_auth_custom_roles || []
|
||||
|
||||
const initialValues = { version: data?.target_upgrade_versions?.[0]?.postgres_version ?? 0 }
|
||||
const { mutate: upgradeProject, isLoading: isUpgrading } = useProjectUpgradeMutation({
|
||||
onSuccess: (res, variables) => {
|
||||
setProjectStatus(queryClient, variables.ref, PROJECT_STATUS.UPGRADING)
|
||||
toast.success('Upgrading project')
|
||||
router.push(`/project/${variables.ref}?upgradeInitiated=true`)
|
||||
router.push(`/project/${variables.ref}?upgradeInitiated=true&trackingId=${res.tracking_id}`)
|
||||
},
|
||||
})
|
||||
|
||||
const onConfirmUpgrade = async (values: any) => {
|
||||
const onConfirmUpgrade = async (values: z.infer<typeof FormSchema>) => {
|
||||
if (!ref) return toast.error('Project ref not found')
|
||||
upgradeProject({ ref, target_version: values.version })
|
||||
|
||||
const { postgresVersionSelection } = values
|
||||
|
||||
const versionDetails = extractPostgresVersionDetails(postgresVersionSelection)
|
||||
if (!versionDetails) return toast.error('Invalid Postgres version')
|
||||
|
||||
upgradeProject({
|
||||
ref,
|
||||
target_version: versionDetails.postgresEngine,
|
||||
release_channel: versionDetails.releaseChannel,
|
||||
})
|
||||
}
|
||||
|
||||
const FormSchema = z.object({
|
||||
postgresVersionSelection: z.string(),
|
||||
})
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
postgresVersionSelection: '',
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const defaultValue = data?.target_upgrade_versions?.[0]
|
||||
? formatValue(data.target_upgrade_versions[0])
|
||||
: ''
|
||||
form.setValue('postgresVersionSelection', defaultValue)
|
||||
}, [data, form])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Alert_Shadcn_ title="Your project can be upgraded to the latest version of Postgres">
|
||||
@@ -86,144 +147,146 @@ const ProjectUpgradeAlert = () => {
|
||||
|
||||
<Modal
|
||||
hideFooter
|
||||
size="small"
|
||||
visible={showUpgradeModal}
|
||||
onCancel={() => setShowUpgradeModal(false)}
|
||||
header={<h3>Upgrade Postgres</h3>}
|
||||
header="Confirm to upgrade Postgres version"
|
||||
>
|
||||
<Form id={formId} initialValues={initialValues} onSubmit={onConfirmUpgrade}>
|
||||
{({ values }: { values: any }) => {
|
||||
const selectedVersion = (data?.target_upgrade_versions ?? []).find(
|
||||
(x) => x.postgres_version === values.version
|
||||
)
|
||||
<Form_Shadcn_ {...form}>
|
||||
<form onSubmit={form.handleSubmit(onConfirmUpgrade)}>
|
||||
<Admonition
|
||||
type="warning"
|
||||
className="border-x-0 border-t-0 rounded-none"
|
||||
title={`Your project will be offline for up to ${durationEstimateHours} hour${durationEstimateHours === 1 ? '' : 's'}`}
|
||||
description="It is advised to upgrade at a time when there will be minimal impact for your application."
|
||||
/>
|
||||
<Modal.Content>
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm">
|
||||
All services will be offline and you will not be able to downgrade back to
|
||||
Postgres {currentPgVersion}.
|
||||
</p>
|
||||
{(data?.potential_breaking_changes ?? []).length > 0 && (
|
||||
<Alert_Shadcn_ variant="destructive" title="Breaking changes">
|
||||
<AlertCircle className="h-4 w-4" strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>Breaking changes</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_ className="flex flex-col gap-3">
|
||||
<p>
|
||||
Your project will be upgraded across major versions of Postgres. This may
|
||||
involve breaking changes.
|
||||
</p>
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal.Content>
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm">All services are going offline.</p>
|
||||
<p className="text-sm">
|
||||
You will not be able to downgrade back to Postgres {currentPgVersion}.
|
||||
</p>
|
||||
<Alert_Shadcn_ title="Your project will be offline while the upgrade is in progress">
|
||||
<AlertCircle className="h-4 w-4" strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>
|
||||
Your project will be offline for up to {durationEstimateHours} hour
|
||||
{durationEstimateHours === 1 ? '' : 's'}
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
<p>
|
||||
It is advised to upgrade at a time when there will be minimal impact for
|
||||
your application.
|
||||
</p>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
{(data?.potential_breaking_changes ?? []).length > 0 && (
|
||||
<Alert_Shadcn_ variant="destructive" title="Breaking changes">
|
||||
<AlertCircle className="h-4 w-4" strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>Breaking changes</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_ className="flex flex-col gap-3">
|
||||
<p>
|
||||
Your project will be upgraded across major versions of Postgres. This
|
||||
may involve breaking changes.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<Button size="tiny" type="default" asChild>
|
||||
<Link
|
||||
href="https://supabase.com/docs/guides/platform/migrating-and-upgrading-projects#caveats"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View docs
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
{legacyAuthCustomRoles.length > 0 && (
|
||||
<Alert_Shadcn_
|
||||
variant="warning"
|
||||
title="Custom Postgres roles using md5 authentication have been detected"
|
||||
>
|
||||
<AlertTriangle className="h-4 w-4" strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>
|
||||
Custom Postgres roles will not work automatically after upgrade
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_ className="flex flex-col gap-3">
|
||||
<p>You must run a series of commands after upgrading.</p>
|
||||
<p>
|
||||
This is because new Postgres versions use scram-sha-256 authentication
|
||||
by default and do not support md5, as it has been deprecated.
|
||||
</p>
|
||||
<div>
|
||||
<p className="mb-1">Run the following commands after the upgrade:</p>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<code className="text-xs">
|
||||
{legacyAuthCustomRoles.map((role) => (
|
||||
<div key={role} className="pb-1">
|
||||
ALTER ROLE <span className="text-brand">{role}</span> WITH
|
||||
PASSWORD '<span className="text-brand">newpassword</span>';
|
||||
</div>
|
||||
))}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Button size="tiny" type="default" asChild>
|
||||
<Link
|
||||
href="https://supabase.com/docs/guides/platform/migrating-and-upgrading-projects#caveats"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View docs
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
<Listbox
|
||||
id="version"
|
||||
name="version"
|
||||
label="Select the version of Postgres to upgrade to"
|
||||
descriptionText={`Postgres will be upgraded from ${currentPgVersion} to ${
|
||||
selectedVersion?.app_version?.split('supabase-postgres-')[1] ??
|
||||
values.version
|
||||
}`}
|
||||
>
|
||||
{data?.target_upgrade_versions.map((version) => (
|
||||
<Listbox.Option
|
||||
key={version.postgres_version}
|
||||
value={version.postgres_version}
|
||||
label={`${version.postgres_version} (${
|
||||
version.app_version.split('supabase-postgres-')[1]
|
||||
})`}
|
||||
>
|
||||
{version.postgres_version} (
|
||||
{version.app_version.split('supabase-postgres-')[1]})
|
||||
</Listbox.Option>
|
||||
))}
|
||||
</Listbox>
|
||||
</div>
|
||||
</Modal.Content>
|
||||
<Modal.Separator />
|
||||
<Modal.Content className="flex items-center space-x-2 justify-end">
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => setShowUpgradeModal(false)}
|
||||
disabled={isUpgrading}
|
||||
<div>
|
||||
<Button size="tiny" type="default" asChild>
|
||||
<Link
|
||||
href="https://supabase.com/docs/guides/platform/migrating-and-upgrading-projects#caveats"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View docs
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
{legacyAuthCustomRoles.length > 0 && (
|
||||
<Alert_Shadcn_
|
||||
variant="warning"
|
||||
title="Custom Postgres roles using md5 authentication have been detected"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button htmlType="submit" disabled={isUpgrading} loading={isUpgrading}>
|
||||
Confirm upgrade
|
||||
</Button>
|
||||
</Modal.Content>
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</Form>
|
||||
<AlertTriangle className="h-4 w-4" strokeWidth={2} />
|
||||
<AlertTitle_Shadcn_>
|
||||
Custom Postgres roles will not work automatically after upgrade
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_ className="flex flex-col gap-3">
|
||||
<p>You must run a series of commands after upgrading.</p>
|
||||
<p>
|
||||
This is because new Postgres versions use scram-sha-256 authentication by
|
||||
default and do not support md5, as it has been deprecated.
|
||||
</p>
|
||||
<div>
|
||||
<p className="mb-1">Run the following commands after the upgrade:</p>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<code className="text-xs">
|
||||
{legacyAuthCustomRoles.map((role) => (
|
||||
<div key={role} className="pb-1">
|
||||
ALTER ROLE <span className="text-brand">{role}</span> WITH PASSWORD
|
||||
'<span className="text-brand">newpassword</span>';
|
||||
</div>
|
||||
))}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Button size="tiny" type="default" asChild>
|
||||
<Link
|
||||
href="https://supabase.com/docs/guides/platform/migrating-and-upgrading-projects#caveats"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View docs
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)}
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="postgresVersionSelection"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout label="Select the version of Postgres to upgrade to">
|
||||
<FormControl_Shadcn_>
|
||||
<Select_Shadcn_ value={field.value} onValueChange={field.onChange}>
|
||||
<SelectTrigger_Shadcn_>
|
||||
<SelectValue_Shadcn_ placeholder="Select a Postgres version" />
|
||||
</SelectTrigger_Shadcn_>
|
||||
<SelectContent_Shadcn_>
|
||||
<SelectGroup_Shadcn_>
|
||||
{(data?.target_upgrade_versions || [])?.map((value) => {
|
||||
const postgresVersion =
|
||||
value.app_version.split('supabase-postgres-')[1]
|
||||
return (
|
||||
<SelectItem_Shadcn_
|
||||
key={formatValue(value)}
|
||||
value={formatValue(value)}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-foreground">{postgresVersion}</span>
|
||||
{value.release_channel !== 'ga' && (
|
||||
<Badge variant="warning" className="mr-1 capitalize">
|
||||
{value.release_channel}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem_Shadcn_>
|
||||
)
|
||||
})}
|
||||
</SelectGroup_Shadcn_>
|
||||
</SelectContent_Shadcn_>
|
||||
</Select_Shadcn_>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</Modal.Content>
|
||||
<Modal.Separator />
|
||||
<Modal.Content className="flex items-center space-x-2 justify-end">
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => setShowUpgradeModal(false)}
|
||||
disabled={isUpgrading}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button htmlType="submit" disabled={isUpgrading} loading={isUpgrading}>
|
||||
Confirm upgrade
|
||||
</Button>
|
||||
</Modal.Content>
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -42,9 +42,14 @@ const InfrastructureInfo = () => {
|
||||
projectRef: ref,
|
||||
})
|
||||
const { data: databases } = useReadReplicasQuery({ projectRef: ref })
|
||||
const { current_app_version, latest_app_version } = data || {}
|
||||
const { current_app_version, current_app_version_release_channel, latest_app_version } =
|
||||
data || {}
|
||||
const isOnLatestVersion = current_app_version === latest_app_version
|
||||
const currentPgVersion = (current_app_version ?? '').split('supabase-postgres-')[1]
|
||||
const isOnNonGenerallyAvailableReleaseChannel =
|
||||
current_app_version_release_channel && current_app_version_release_channel !== 'ga'
|
||||
? current_app_version_release_channel
|
||||
: undefined
|
||||
const latestPgVersion = (latest_app_version ?? '').split('supabase-postgres-')[1]
|
||||
|
||||
const isInactive = project?.status === 'INACTIVE'
|
||||
@@ -106,6 +111,19 @@ const InfrastructureInfo = () => {
|
||||
value={currentPgVersion}
|
||||
label="Postgres version"
|
||||
actions={[
|
||||
isOnNonGenerallyAvailableReleaseChannel && (
|
||||
<Tooltip_Shadcn_>
|
||||
<TooltipTrigger_Shadcn_>
|
||||
<Badge variant="warning" className="mr-1 capitalize">
|
||||
{isOnNonGenerallyAvailableReleaseChannel}
|
||||
</Badge>
|
||||
</TooltipTrigger_Shadcn_>
|
||||
<TooltipContent_Shadcn_ side="bottom" className="w-44 text-center">
|
||||
This project uses a {isOnNonGenerallyAvailableReleaseChannel} database
|
||||
version release
|
||||
</TooltipContent_Shadcn_>
|
||||
</Tooltip_Shadcn_>
|
||||
),
|
||||
isOnLatestVersion && (
|
||||
<Tooltip_Shadcn_>
|
||||
<TooltipTrigger_Shadcn_>
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import dayjs from 'dayjs'
|
||||
import { ExternalLink, PauseCircle } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { toast } from 'sonner'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import {
|
||||
PostgresEngine,
|
||||
ProjectUnpausePostgresVersion,
|
||||
ReleaseChannel,
|
||||
useProjectUnpausePostgresVersionsQuery,
|
||||
} from 'data/config/project-unpause-postgres-versions-query'
|
||||
import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-limit-check-query'
|
||||
import { useProjectPauseStatusQuery } from 'data/projects/project-pause-status-query'
|
||||
import { useProjectRestoreMutation } from 'data/projects/project-restore-mutation'
|
||||
@@ -18,8 +27,24 @@ import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { PROJECT_STATUS } from 'lib/constants'
|
||||
import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button, Modal } from 'ui'
|
||||
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
|
||||
import {
|
||||
AlertDescription_Shadcn_,
|
||||
AlertTitle_Shadcn_,
|
||||
Alert_Shadcn_,
|
||||
Badge,
|
||||
Button,
|
||||
FormControl_Shadcn_,
|
||||
FormField_Shadcn_,
|
||||
Form_Shadcn_,
|
||||
Modal,
|
||||
SelectContent_Shadcn_,
|
||||
SelectGroup_Shadcn_,
|
||||
SelectItem_Shadcn_,
|
||||
SelectTrigger_Shadcn_,
|
||||
SelectValue_Shadcn_,
|
||||
Select_Shadcn_,
|
||||
} from 'ui'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
import { PauseDisabledState } from './PauseDisabledState'
|
||||
import { RestorePaidPlanProjectNotice } from '../RestorePaidPlanProjectNotice'
|
||||
@@ -29,12 +54,27 @@ export interface ProjectPausedStateProps {
|
||||
product?: string
|
||||
}
|
||||
|
||||
interface PostgresVersionDetails {
|
||||
postgresEngine: PostgresEngine
|
||||
releaseChannel: ReleaseChannel
|
||||
}
|
||||
|
||||
const formatValue = ({ postgres_engine, release_channel }: ProjectUnpausePostgresVersion) => {
|
||||
return `${postgres_engine}|${release_channel}`
|
||||
}
|
||||
|
||||
export const extractPostgresVersionDetails = (value: string): PostgresVersionDetails => {
|
||||
const [postgresEngine, releaseChannel] = value.split('|')
|
||||
return { postgresEngine, releaseChannel } as PostgresVersionDetails
|
||||
}
|
||||
|
||||
export const ProjectPausedState = ({ product }: ProjectPausedStateProps) => {
|
||||
const { ref } = useParams()
|
||||
const queryClient = useQueryClient()
|
||||
const { project } = useProjectContext()
|
||||
const selectedOrganization = useSelectedOrganization()
|
||||
const enforceNinetyDayUnpauseExpiry = useFlag('enforceNinetyDayUnpauseExpiry')
|
||||
const projectVersionSelectionDisabled = useFlag('disableProjectVersionSelection')
|
||||
|
||||
const orgSlug = selectedOrganization?.slug
|
||||
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug })
|
||||
@@ -64,11 +104,15 @@ export const ProjectPausedState = ({ product }: ProjectPausedStateProps) => {
|
||||
{ enabled: isFreePlan }
|
||||
)
|
||||
|
||||
const { data: availablePostgresVersions } = useProjectUnpausePostgresVersionsQuery({
|
||||
projectRef: project?.ref,
|
||||
})
|
||||
|
||||
const hasMembersExceedingFreeTierLimit = (membersExceededLimit || []).length > 0
|
||||
const [showConfirmRestore, setShowConfirmRestore] = useState(false)
|
||||
const [showFreeProjectLimitWarning, setShowFreeProjectLimitWarning] = useState(false)
|
||||
|
||||
const { mutate: restoreProject } = useProjectRestoreMutation({
|
||||
const { mutate: restoreProject, isLoading: isRestoring } = useProjectRestoreMutation({
|
||||
onSuccess: (_, variables) => {
|
||||
setProjectStatus(queryClient, variables.ref, PROJECT_STATUS.RESTORING)
|
||||
toast.success('Restoring project')
|
||||
@@ -87,13 +131,45 @@ export const ProjectPausedState = ({ product }: ProjectPausedStateProps) => {
|
||||
else setShowConfirmRestore(true)
|
||||
}
|
||||
|
||||
const onConfirmRestore = () => {
|
||||
const onConfirmRestore = async (values: z.infer<typeof FormSchema>) => {
|
||||
if (!project) {
|
||||
return toast.error('Unable to restore: project is required')
|
||||
}
|
||||
restoreProject({ ref: project.ref })
|
||||
|
||||
if (projectVersionSelectionDisabled) {
|
||||
restoreProject({ ref: project.ref })
|
||||
} else {
|
||||
const { postgresVersionSelection } = values
|
||||
|
||||
const postgresVersionDetails = extractPostgresVersionDetails(postgresVersionSelection)
|
||||
|
||||
restoreProject({
|
||||
ref: project.ref,
|
||||
releaseChannel: postgresVersionDetails.releaseChannel,
|
||||
postgresEngine: postgresVersionDetails.postgresEngine,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const FormSchema = z.object({
|
||||
postgresVersionSelection: z.string(),
|
||||
})
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
postgresVersionSelection: '',
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const defaultValue = availablePostgresVersions?.available_versions[0]
|
||||
? formatValue(availablePostgresVersions?.available_versions[0])
|
||||
: ''
|
||||
form.setValue('postgresVersionSelection', defaultValue)
|
||||
}, [availablePostgresVersions, form])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-4">
|
||||
@@ -224,15 +300,78 @@ export const ProjectPausedState = ({ product }: ProjectPausedStateProps) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConfirmModal
|
||||
<Modal
|
||||
hideFooter
|
||||
visible={showConfirmRestore}
|
||||
size={'small'}
|
||||
title="Restore this project"
|
||||
description="Confirm to restore this project? Your project's data will be restored to when it was initially paused."
|
||||
buttonLabel="Restore project"
|
||||
buttonLoadingLabel="Restoring project"
|
||||
onSelectCancel={() => setShowConfirmRestore(false)}
|
||||
onSelectConfirm={onConfirmRestore}
|
||||
/>
|
||||
onCancel={() => setShowConfirmRestore(false)}
|
||||
header={'Restore this project'}
|
||||
>
|
||||
<Form_Shadcn_ {...form}>
|
||||
<form onSubmit={form.handleSubmit(onConfirmRestore)}>
|
||||
{!projectVersionSelectionDisabled && (
|
||||
<Modal.Content>
|
||||
<div className="space-y-2">
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="postgresVersionSelection"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout label="Select the version of Postgres to restore to">
|
||||
<FormControl_Shadcn_>
|
||||
<Select_Shadcn_ value={field.value} onValueChange={field.onChange}>
|
||||
<SelectTrigger_Shadcn_>
|
||||
<SelectValue_Shadcn_ placeholder="Select a Postgres version" />
|
||||
</SelectTrigger_Shadcn_>
|
||||
<SelectContent_Shadcn_>
|
||||
<SelectGroup_Shadcn_>
|
||||
{(availablePostgresVersions?.available_versions || [])?.map(
|
||||
(value) => {
|
||||
const postgresVersion =
|
||||
value.version.split('supabase-postgres-')[1]
|
||||
return (
|
||||
<SelectItem_Shadcn_
|
||||
key={formatValue(value)}
|
||||
value={formatValue(value)}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-foreground">{postgresVersion}</span>
|
||||
{value.release_channel !== 'ga' && (
|
||||
<Badge variant="warning" className="mr-1 capitalize">
|
||||
{value.release_channel}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem_Shadcn_>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</SelectGroup_Shadcn_>
|
||||
</SelectContent_Shadcn_>
|
||||
</Select_Shadcn_>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</Modal.Content>
|
||||
)}
|
||||
<Modal.Content className="flex items-center space-x-2 justify-end">
|
||||
<Button
|
||||
type="default"
|
||||
disabled={isRestoring}
|
||||
onClick={() => setShowConfirmRestore(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button htmlType="submit" loading={isRestoring}>
|
||||
Confirm upgrade
|
||||
</Button>
|
||||
</Modal.Content>
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
hideFooter
|
||||
|
||||
@@ -22,9 +22,11 @@ import {
|
||||
Loader,
|
||||
Check,
|
||||
} from 'lucide-react'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
|
||||
const UpgradingState = () => {
|
||||
const { ref } = useParams()
|
||||
const queryParams = useSearchParams()
|
||||
const queryClient = useQueryClient()
|
||||
const { project } = useProjectContext()
|
||||
const [loading, setLoading] = useState(false)
|
||||
@@ -33,6 +35,7 @@ const UpgradingState = () => {
|
||||
{
|
||||
projectRef: ref,
|
||||
projectStatus: project?.status,
|
||||
trackingId: queryParams.get('trackingId'),
|
||||
},
|
||||
{
|
||||
enabled: IS_PLATFORM,
|
||||
|
||||
@@ -15,4 +15,12 @@ export const configKeys = {
|
||||
['projects', projectRef, 'disk-attributes'] as const,
|
||||
diskUtilization: (projectRef: string | undefined) =>
|
||||
['projects', projectRef, 'disk-utilization'] as const,
|
||||
projectCreationPostgresVersions: (
|
||||
organizationSlug: string | undefined,
|
||||
cloudProvider: string,
|
||||
dbRegion: string
|
||||
) =>
|
||||
['projects', organizationSlug, cloudProvider, dbRegion, 'available-creation-versions'] as const,
|
||||
projectUnpausePostgresVersions: (projectRef: string | undefined) =>
|
||||
['projects', projectRef, 'available-unpause-versions'] as const,
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
|
||||
import { components } from 'api-types'
|
||||
import { handleError, post } from 'data/fetchers'
|
||||
import { CloudProvider } from 'shared-data'
|
||||
import type { ResponseError } from 'types'
|
||||
import { configKeys } from './keys'
|
||||
|
||||
export type ProjectCreationPostgresVersionsVariables = {
|
||||
cloudProvider: CloudProvider
|
||||
dbRegion: string
|
||||
organizationSlug: string | undefined
|
||||
}
|
||||
|
||||
export type ProjectCreationPostgresVersion = components['schemas']['ProjectCreationVersionInfo']
|
||||
|
||||
export type ProjectCreationPostgresVersionsResponse = {
|
||||
available_versions: ProjectCreationPostgresVersion[]
|
||||
}
|
||||
|
||||
export async function getPostgresCreationVersions(
|
||||
{ cloudProvider, dbRegion, organizationSlug }: ProjectCreationPostgresVersionsVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
if (!organizationSlug) throw new Error('organizationSlug is required')
|
||||
|
||||
const { data, error } = await post('/platform/organizations/{slug}/available-versions', {
|
||||
params: { path: { slug: organizationSlug } },
|
||||
body: { provider: cloudProvider, region: dbRegion },
|
||||
signal,
|
||||
})
|
||||
|
||||
if (error) handleError(error)
|
||||
return data as ProjectCreationPostgresVersionsResponse
|
||||
}
|
||||
|
||||
export type ProjectCreationPostgresVersionData = Awaited<
|
||||
ReturnType<typeof getPostgresCreationVersions>
|
||||
>
|
||||
export type ProjectCreationPostgresVersionError = ResponseError
|
||||
|
||||
export const useProjectCreationPostgresVersionsQuery = <TData = ProjectCreationPostgresVersionData>(
|
||||
{ cloudProvider, dbRegion, organizationSlug }: ProjectCreationPostgresVersionsVariables,
|
||||
{
|
||||
enabled = true,
|
||||
...options
|
||||
}: UseQueryOptions<
|
||||
ProjectCreationPostgresVersionData,
|
||||
ProjectCreationPostgresVersionError,
|
||||
TData
|
||||
> = {}
|
||||
) => {
|
||||
return useQuery<ProjectCreationPostgresVersionData, ProjectCreationPostgresVersionError, TData>(
|
||||
configKeys.projectCreationPostgresVersions(organizationSlug, cloudProvider, dbRegion),
|
||||
({ signal }) =>
|
||||
getPostgresCreationVersions({ organizationSlug, cloudProvider, dbRegion }, signal),
|
||||
{
|
||||
enabled: enabled && typeof organizationSlug !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
|
||||
import { get, handleError } from 'data/fetchers'
|
||||
import { configKeys } from './keys'
|
||||
import { components } from 'api-types'
|
||||
import { ResponseError } from 'types'
|
||||
|
||||
export type ProjectUnpausePostgresVersionsVariables = {
|
||||
projectRef?: string
|
||||
}
|
||||
|
||||
export type ProjectUnpausePostgresVersion = components['schemas']['ProjectUnpauseVersionInfo']
|
||||
export type ReleaseChannel = components['schemas']['ReleaseChannel']
|
||||
export type PostgresEngine = components['schemas']['PostgresEngine']
|
||||
|
||||
export type ProjectUnpausePostgresVersionsResponse = {
|
||||
available_versions: ProjectUnpausePostgresVersion[]
|
||||
}
|
||||
|
||||
export async function getPostgresUnpauseVersions(
|
||||
{ projectRef }: ProjectUnpausePostgresVersionsVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
if (!projectRef) throw new Error('projectRef is required')
|
||||
|
||||
const { data, error } = await get('/platform/projects/{ref}/restore/versions', {
|
||||
params: { path: { ref: projectRef } },
|
||||
signal,
|
||||
})
|
||||
|
||||
if (error) handleError(error)
|
||||
return data as ProjectUnpausePostgresVersionsResponse
|
||||
}
|
||||
|
||||
export type ProjectUnpausePostgresVersionData = Awaited<
|
||||
ReturnType<typeof getPostgresUnpauseVersions>
|
||||
>
|
||||
export type ProjectUnpausePostgresVersionError = ResponseError
|
||||
|
||||
export const useProjectUnpausePostgresVersionsQuery = <TData = ProjectUnpausePostgresVersionData>(
|
||||
{ projectRef }: ProjectUnpausePostgresVersionsVariables,
|
||||
{
|
||||
enabled = true,
|
||||
...options
|
||||
}: UseQueryOptions<
|
||||
ProjectUnpausePostgresVersionData,
|
||||
ProjectUnpausePostgresVersionError,
|
||||
TData
|
||||
> = {}
|
||||
) => {
|
||||
return useQuery<ProjectUnpausePostgresVersionData, ProjectUnpausePostgresVersionError, TData>(
|
||||
configKeys.projectUnpausePostgresVersions(projectRef),
|
||||
({ signal }) => getPostgresUnpauseVersions({ projectRef }, signal),
|
||||
{
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import type { ResponseError } from 'types'
|
||||
import { configKeys } from './keys'
|
||||
import { components } from 'api-types'
|
||||
|
||||
export type ProjectUpgradeTargetVersion = components['schemas']['ProjectVersion']
|
||||
export type ProjectUpgradeEligibilityVariables = { projectRef?: string }
|
||||
export type ProjectUpgradeEligibilityResponse =
|
||||
components['schemas']['ProjectUpgradeEligibilityResponse']
|
||||
|
||||
@@ -7,16 +7,22 @@ import { configKeys } from './keys'
|
||||
export type ProjectUpgradingStatusVariables = {
|
||||
projectRef?: string
|
||||
projectStatus?: string
|
||||
trackingId?: string | null
|
||||
}
|
||||
|
||||
export async function getProjectUpgradingStatus(
|
||||
{ projectRef }: ProjectUpgradingStatusVariables,
|
||||
{ projectRef, trackingId }: ProjectUpgradingStatusVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
if (!projectRef) throw new Error('projectRef is required')
|
||||
|
||||
const queryParams: Record<string, string> = {}
|
||||
if (trackingId) {
|
||||
queryParams['tracking_id'] = trackingId
|
||||
}
|
||||
|
||||
const { data, error } = await get(`/v1/projects/{ref}/upgrade/status`, {
|
||||
params: { path: { ref: projectRef } },
|
||||
params: { path: { ref: projectRef }, query: queryParams },
|
||||
signal,
|
||||
})
|
||||
if (error) handleError(error)
|
||||
@@ -28,7 +34,7 @@ export type ProjectUpgradingStatusData = Awaited<ReturnType<typeof getProjectUpg
|
||||
export type ProjectUpgradingStatusError = unknown
|
||||
|
||||
export const useProjectUpgradingStatusQuery = <TData = ProjectUpgradingStatusData>(
|
||||
{ projectRef, projectStatus }: ProjectUpgradingStatusVariables,
|
||||
{ projectRef, projectStatus, trackingId }: ProjectUpgradingStatusVariables,
|
||||
{
|
||||
enabled = true,
|
||||
...options
|
||||
@@ -38,7 +44,7 @@ export const useProjectUpgradingStatusQuery = <TData = ProjectUpgradingStatusDat
|
||||
|
||||
return useQuery<ProjectUpgradingStatusData, ProjectUpgradingStatusError, TData>(
|
||||
configKeys.upgradeStatus(projectRef),
|
||||
({ signal }) => getProjectUpgradingStatus({ projectRef }, signal),
|
||||
({ signal }) => getProjectUpgradingStatus({ projectRef, trackingId }, signal),
|
||||
{
|
||||
enabled: enabled && typeof projectRef !== 'undefined',
|
||||
refetchInterval(data) {
|
||||
|
||||
@@ -18,6 +18,8 @@ const WHITELIST_ERRORS = [
|
||||
]
|
||||
|
||||
export type DbInstanceSize = components['schemas']['DesiredInstanceSize']
|
||||
export type ReleaseChannel = components['schemas']['ReleaseChannel']
|
||||
export type PostgresEngine = components['schemas']['PostgresEngine']
|
||||
|
||||
export type ProjectCreateVariables = {
|
||||
name: string
|
||||
@@ -32,6 +34,8 @@ export type ProjectCreateVariables = {
|
||||
dbInstanceSize?: DbInstanceSize
|
||||
dataApiExposedSchemas?: string[]
|
||||
dataApiUseApiSchema?: boolean
|
||||
postgresEngine?: PostgresEngine
|
||||
releaseChannel?: ReleaseChannel
|
||||
}
|
||||
|
||||
export async function createProject({
|
||||
@@ -46,6 +50,8 @@ export async function createProject({
|
||||
dbInstanceSize,
|
||||
dataApiExposedSchemas,
|
||||
dataApiUseApiSchema,
|
||||
postgresEngine,
|
||||
releaseChannel,
|
||||
}: ProjectCreateVariables) {
|
||||
const body: components['schemas']['CreateProjectBody'] = {
|
||||
cloud_provider: cloudProvider,
|
||||
@@ -61,6 +67,8 @@ export async function createProject({
|
||||
desired_instance_size: dbInstanceSize,
|
||||
data_api_exposed_schemas: dataApiExposedSchemas,
|
||||
data_api_use_api_schema: dataApiUseApiSchema,
|
||||
postgres_engine: postgresEngine,
|
||||
release_channel: releaseChannel,
|
||||
}
|
||||
|
||||
const { data, error } = await post(`/platform/projects`, {
|
||||
|
||||
@@ -3,16 +3,28 @@ import { toast } from 'sonner'
|
||||
|
||||
import { handleError, post } from 'data/fetchers'
|
||||
import type { ResponseError } from 'types'
|
||||
import { components } from 'api-types'
|
||||
|
||||
export type ReleaseChannel = components['schemas']['ReleaseChannel']
|
||||
export type PostgresEngine = components['schemas']['PostgresEngine']
|
||||
|
||||
export type ProjectRestoreVariables = {
|
||||
ref: string
|
||||
postgresEngine?: PostgresEngine
|
||||
releaseChannel?: ReleaseChannel
|
||||
}
|
||||
|
||||
export async function restoreProject({ ref }: ProjectRestoreVariables) {
|
||||
export async function restoreProject({
|
||||
ref,
|
||||
postgresEngine,
|
||||
releaseChannel,
|
||||
}: ProjectRestoreVariables) {
|
||||
const { data, error } = await post('/platform/projects/{ref}/restore', {
|
||||
params: { path: { ref } },
|
||||
// @ts-ignore
|
||||
body: {},
|
||||
body: {
|
||||
postgres_engine: postgresEngine,
|
||||
release_channel: releaseChannel,
|
||||
},
|
||||
})
|
||||
if (error) handleError(error)
|
||||
return data
|
||||
|
||||
@@ -3,17 +3,24 @@ import { toast } from 'sonner'
|
||||
|
||||
import { handleError, post } from 'data/fetchers'
|
||||
import type { ResponseError } from 'types'
|
||||
import { components } from 'api-types'
|
||||
|
||||
export type ReleaseChannel = components['schemas']['ReleaseChannel']
|
||||
|
||||
export type ProjectUpgradeVariables = {
|
||||
ref: string
|
||||
target_version: number
|
||||
target_version: string
|
||||
release_channel: ReleaseChannel
|
||||
}
|
||||
|
||||
export async function upgradeProject({ ref, target_version }: ProjectUpgradeVariables) {
|
||||
export async function upgradeProject({
|
||||
ref,
|
||||
target_version,
|
||||
release_channel,
|
||||
}: ProjectUpgradeVariables) {
|
||||
const { data, error } = await post('/v1/projects/{ref}/upgrade', {
|
||||
params: { path: { ref } },
|
||||
// @ts-expect-error release_channel param is optional on API end, but not typed properly from generated types
|
||||
body: { target_version: target_version.toString() },
|
||||
body: { target_version: target_version.toString(), release_channel },
|
||||
})
|
||||
if (error) handleError(error)
|
||||
return data
|
||||
|
||||
Vendored
+1
-1
@@ -2,4 +2,4 @@
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
@@ -17,6 +17,10 @@ import {
|
||||
FreeProjectLimitWarning,
|
||||
NotOrganizationOwnerWarning,
|
||||
} from 'components/interfaces/Organization/NewProject'
|
||||
import {
|
||||
PostgresVersionSelector,
|
||||
extractPostgresVersionDetails,
|
||||
} from 'components/interfaces/ProjectCreation/PostgresVersionSelector'
|
||||
import { RegionSelector } from 'components/interfaces/ProjectCreation/RegionSelector'
|
||||
import { WizardLayoutWithoutAuth } from 'components/layouts/WizardLayout'
|
||||
import DisabledWarningDueToIncident from 'components/ui/DisabledWarningDueToIncident'
|
||||
@@ -194,6 +198,7 @@ const Wizard: NextPageWithLayout = () => {
|
||||
const { slug, projectName } = useParams()
|
||||
|
||||
const projectCreationDisabled = useFlag('disableProjectCreationAndUpdate')
|
||||
const projectVersionSelectionDisabled = useFlag('disableProjectVersionSelection')
|
||||
const cloudProviderEnabled = useFlag('enableFlyCloudProvider')
|
||||
const { data: membersExceededLimit, isLoading: isLoadingFreeProjectLimitCheck } =
|
||||
useFreeProjectLimitCheckQuery({ slug })
|
||||
@@ -301,6 +306,7 @@ const Wizard: NextPageWithLayout = () => {
|
||||
instanceSize: z.string(),
|
||||
dataApi: z.boolean(),
|
||||
useApiSchema: z.boolean(),
|
||||
postgresVersionSelection: z.string(),
|
||||
})
|
||||
.superRefine(({ dbPassStrength }, refinementContext) => {
|
||||
if (dbPassStrength < DEFAULT_MINIMUM_PASSWORD_STRENGTH) {
|
||||
@@ -326,6 +332,7 @@ const Wizard: NextPageWithLayout = () => {
|
||||
instanceSize: sizes[0],
|
||||
dataApi: true,
|
||||
useApiSchema: false,
|
||||
postgresVersionSelection: '',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -368,8 +375,12 @@ const Wizard: NextPageWithLayout = () => {
|
||||
instanceSize,
|
||||
dataApi,
|
||||
useApiSchema,
|
||||
postgresVersionSelection,
|
||||
} = values
|
||||
|
||||
const { postgresEngine, releaseChannel } =
|
||||
extractPostgresVersionDetails(postgresVersionSelection)
|
||||
|
||||
const data: ProjectCreateVariables = {
|
||||
cloudProvider: cloudProvider,
|
||||
organizationId: currentOrg.id,
|
||||
@@ -383,6 +394,8 @@ const Wizard: NextPageWithLayout = () => {
|
||||
orgSubscription?.plan.id === 'free' ? undefined : (instanceSize as DesiredInstanceSize),
|
||||
dataApiExposedSchemas: !dataApi ? [] : undefined,
|
||||
dataApiUseApiSchema: !dataApi ? false : useApiSchema,
|
||||
postgresEngine: postgresEngine,
|
||||
releaseChannel: releaseChannel,
|
||||
}
|
||||
if (postgresVersion) {
|
||||
if (!postgresVersion.match(/1[2-9]\..*/)) {
|
||||
@@ -546,30 +559,6 @@ const Wizard: NextPageWithLayout = () => {
|
||||
/>
|
||||
</Panel.Content>
|
||||
|
||||
{showNonProdFields && (
|
||||
<Panel.Content>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="postgresVersion"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
label="Postgres version"
|
||||
layout="horizontal"
|
||||
description="Specify a custom version of Postgres (Defaults to the latest). This is only applicable for local/staging projects"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
placeholder="Postgres version"
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</Panel.Content>
|
||||
)}
|
||||
|
||||
{cloudProviderEnabled && showNonProdFields && (
|
||||
<Panel.Content>
|
||||
<FormField_Shadcn_
|
||||
@@ -623,19 +612,30 @@ const Wizard: NextPageWithLayout = () => {
|
||||
<FormItemLayout
|
||||
layout="horizontal"
|
||||
label={
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-col gap-y-4">
|
||||
<span>Compute Size</span>
|
||||
|
||||
<div className="flex flex-col space-y-2">
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<Link
|
||||
href="https://supabase.com/docs/guides/platform/compute-add-ons"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://supabase.com/docs/guides/platform/compute-add-ons"
|
||||
>
|
||||
<div className="flex items-center space-x-2 opacity-75 hover:opacity-100 transition">
|
||||
<p className="text-sm m-0">Compute Add-Ons</p>
|
||||
<ExternalLink size={16} strokeWidth={1.5} />
|
||||
</div>
|
||||
</Link>
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://supabase.com/docs/guides/platform/org-based-billing#billing-for-compute-compute-hours"
|
||||
>
|
||||
<div className="flex items-center space-x-2 opacity-75 hover:opacity-100 transition">
|
||||
<p className="text-sm m-0">Compute Billing</p>
|
||||
<ExternalLink size={16} strokeWidth={1.5} />
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -698,26 +698,7 @@ const Wizard: NextPageWithLayout = () => {
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
<FormItemLayout
|
||||
className="pt-4"
|
||||
label={
|
||||
<div className="space-y-4">
|
||||
<span>Compute Billing</span>
|
||||
<div className="flex flex-col space-y-2">
|
||||
<Link
|
||||
href="https://supabase.com/docs/guides/platform/org-based-billing#billing-for-compute-compute-hours"
|
||||
target="_blank"
|
||||
>
|
||||
<div className="flex items-center space-x-2 opacity-75 hover:opacity-100 transition">
|
||||
<p className="text-sm m-0">Docs</p>
|
||||
<ExternalLink size={16} strokeWidth={1.5} />
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
layout="horizontal"
|
||||
>
|
||||
<FormItemLayout layout="horizontal">
|
||||
<div className="flex justify-between mr-2">
|
||||
<span>Additional Monthly Compute Costs</span>
|
||||
<div className="text-brand flex gap-1 items-center">
|
||||
@@ -782,7 +763,7 @@ const Wizard: NextPageWithLayout = () => {
|
||||
</TableBody>
|
||||
</Table>
|
||||
<PopoverSeparator />
|
||||
<Table className="mt-3">
|
||||
<Table>
|
||||
<TableHeader className="[&_th]:h-7">
|
||||
<TableRow>
|
||||
<TableHead colSpan={2}>Compute Credits</TableHead>
|
||||
@@ -813,7 +794,7 @@ const Wizard: NextPageWithLayout = () => {
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<div className="p-4 text-xs text-foreground-light mt-2 space-y-1">
|
||||
<div className="p-4 text-xs text-foreground-light space-y-1">
|
||||
<p>
|
||||
Compute is charged usage-based whenever your billing cycle
|
||||
resets. Given compute charges are hourly, your invoice will
|
||||
@@ -913,6 +894,48 @@ const Wizard: NextPageWithLayout = () => {
|
||||
/>
|
||||
</Panel.Content>
|
||||
|
||||
{!projectVersionSelectionDisabled && (
|
||||
<Panel.Content>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="postgresVersionSelection"
|
||||
render={({ field }) => (
|
||||
<PostgresVersionSelector
|
||||
field={field}
|
||||
form={form}
|
||||
cloudProvider={form.getValues('cloudProvider') as CloudProvider}
|
||||
organizationSlug={slug}
|
||||
dbRegion={form.getValues('dbRegion')}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Panel.Content>
|
||||
)}
|
||||
|
||||
{showNonProdFields && (
|
||||
<Panel.Content>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="postgresVersion"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
label="Custom Postgres version"
|
||||
layout="horizontal"
|
||||
description="Specify a custom version of Postgres (Defaults to the latest). This is only applicable for local/staging projects"
|
||||
>
|
||||
<FormControl_Shadcn_>
|
||||
<Input_Shadcn_
|
||||
placeholder="Postgres version"
|
||||
{...field}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</Panel.Content>
|
||||
)}
|
||||
|
||||
<Panel.Content>
|
||||
<Collapsible_Shadcn_>
|
||||
<CollapsibleTrigger_Shadcn_ className="group/advanced-trigger font-mono uppercase tracking-widest text-xs flex items-center gap-1 text-foreground-lighter/75 hover:text-foreground-light transition data-[state=open]:text-foreground-light">
|
||||
|
||||
Reference in new issue
Block a user