Skip to content

Commit f6692f2

Browse files
dragarciatgallacherjoshenlim
authored
chore: Dashboard guards to limit what can and cannot be done by an AWS_K8S project (supabase#36630)
* chore: transition from AWS_NEW to AWS_K8S * chore: transition from useIsAwsNew to useIsAwsK8s * chore: new useIsAws method * chore: disk management protections * chore: IPV4 addon protections * chore: AWS_K8S restart protection * chore: AWS_K8S read replica protections * chore: restore to new project protections * Update apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx Co-authored-by: Tom Gallacher <[email protected]> * chore: changes based on feedback * fix: prettier * minor fixes * Use disabled state for ipv4 address in ipv4sidepanel isntead of hiding the UI entirely * Revert hardcode in useSelectedProject --------- Co-authored-by: Tom Gallacher <[email protected]> Co-authored-by: Joshen Lim <[email protected]>
1 parent 3a50dd1 commit f6692f2

File tree

10 files changed

+138
-103
lines changed

10 files changed

+138
-103
lines changed

apps/studio/components/interfaces/DiskManagement/DiskManagementForm.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import { DiskCountdownRadial } from './ui/DiskCountdownRadial'
5656
import { DiskType, RESTRICTED_COMPUTE_FOR_THROUGHPUT_ON_GP3 } from './ui/DiskManagement.constants'
5757
import { NoticeBar } from './ui/NoticeBar'
5858
import { SpendCapDisabledSection } from './ui/SpendCapDisabledSection'
59+
import { useIsAwsCloudProvider, useIsAwsK8sCloudProvider } from 'hooks/misc/useSelectedProject'
5960

6061
export function DiskManagementForm() {
6162
// isLoading is used to avoid a useCheckPermissions() race condition
@@ -69,7 +70,8 @@ export function DiskManagementForm() {
6970
(warning) => warning.project === project?.ref
7071
)
7172
const isReadOnlyMode = projectResourceWarnings?.is_readonly_mode_enabled
72-
const isFlyArchitecture = project?.cloud_provider === 'FLY'
73+
const isAws = useIsAwsCloudProvider()
74+
const isAwsK8s = useIsAwsK8sCloudProvider()
7375

7476
/**
7577
* Permissions
@@ -118,27 +120,24 @@ export function DiskManagementForm() {
118120
setRefetchInterval(2000)
119121
}
120122
},
121-
enabled: project != null && !isFlyArchitecture,
123+
enabled: project != null && isAws,
122124
}
123125
)
124126
const { isSuccess: isAddonsSuccess } = useProjectAddonsQuery({ projectRef })
125127
const { isWithinCooldownWindow, isSuccess: isCooldownSuccess } =
126128
useRemainingDurationForDiskAttributeUpdate({
127129
projectRef,
128-
enabled: project != null && !isFlyArchitecture,
130+
enabled: project != null && isAws,
129131
})
130132
const { data: diskUtil, isSuccess: isDiskUtilizationSuccess } = useDiskUtilizationQuery(
131133
{
132134
projectRef,
133135
},
134-
{ enabled: project != null && !isFlyArchitecture }
136+
{ enabled: project != null && isAws }
135137
)
136138

137139
const { data: diskAutoscaleConfig, isSuccess: isDiskAutoscaleConfigSuccess } =
138-
useDiskAutoscaleCustomConfigQuery(
139-
{ projectRef },
140-
{ enabled: project != null && !isFlyArchitecture }
141-
)
140+
useDiskAutoscaleCustomConfigQuery({ projectRef }, { enabled: project != null && isAws })
142141

143142
/**
144143
* Handle default values
@@ -200,7 +199,7 @@ export function DiskManagementForm() {
200199
isPlanUpgradeRequired ||
201200
isWithinCooldownWindow ||
202201
!canUpdateDiskConfiguration ||
203-
isFlyArchitecture
202+
!isAws
204203

205204
const disableComputeInputs = isPlanUpgradeRequired
206205
const isDirty = !!Object.keys(form.formState.dirtyFields).length
@@ -344,15 +343,17 @@ export function DiskManagementForm() {
344343
<SpendCapDisabledSection />
345344
<NoticeBar
346345
type="default"
347-
visible={isFlyArchitecture}
348-
title="Disk configuration is not available on Fly Postgres"
346+
visible={!isAws}
347+
title="Disk configuration is only available for projects in the AWS cloud provider"
349348
description={
350-
isBranch
351-
? 'Delete and recreate your Preview Branch to configure disk size. It was deployed on an older branching infrastructure.'
352-
: 'The Fly Postgres offering is deprecated - please migrate your instance to Supabase to configure your disk.'
349+
isAwsK8s
350+
? 'Configuring your disk for AWS (Revamped) projects is unavailable for now.'
351+
: isBranch
352+
? 'Delete and recreate your Preview Branch to configure disk size. It was deployed on an older branching infrastructure.'
353+
: 'The Fly Postgres offering is deprecated - please migrate your instance to the AWS cloud prov to configure your disk.'
353354
}
354355
/>
355-
{!isFlyArchitecture && (
356+
{isAws && (
356357
<>
357358
<div className="flex flex-col gap-y-3">
358359
<DiskCountdownRadial />

apps/studio/components/interfaces/Settings/Addons/Addons.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-que
3232
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
3333
import type { ProjectAddonVariantMeta } from 'data/subscriptions/types'
3434
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
35-
import { useIsOrioleDb, useIsOrioleDbInAws, useProjectByRef } from 'hooks/misc/useSelectedProject'
35+
import { useIsOrioleDbInAws, useProjectByRef } from 'hooks/misc/useSelectedProject'
3636
import { useFlag } from 'hooks/ui/useFlag'
3737
import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils'
3838
import { BASE_PATH, INSTANCE_MICRO_SPECS, INSTANCE_NANO_SPECS } from 'lib/constants'
@@ -53,7 +53,6 @@ const Addons = () => {
5353
const parentProject = useProjectByRef(selectedProject?.parent_project_ref)
5454
const isBranch = parentProject !== undefined
5555
const isProjectActive = useIsProjectActive()
56-
const isOrioleDb = useIsOrioleDb()
5756
const isOrioleDbInAws = useIsOrioleDbInAws()
5857

5958
const { data: settings } = useProjectSettingsV2Query({ projectRef })

apps/studio/components/interfaces/Settings/Addons/IPv4SidePanel.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { PermissionAction } from '@supabase/shared-types/out/constants'
2+
import { ExternalLink } from 'lucide-react'
23
import Link from 'next/link'
34
import { useEffect, useState } from 'react'
45
import { toast } from 'sonner'
@@ -11,13 +12,14 @@ import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
1112
import type { AddonVariantId } from 'data/subscriptions/types'
1213
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
1314
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
15+
import { useIsAwsCloudProvider } from 'hooks/misc/useSelectedProject'
1416
import { formatCurrency } from 'lib/helpers'
15-
import { ExternalLink } from 'lucide-react'
1617
import { useAddonsPagePanel } from 'state/addons-page'
1718
import { Button, Radio, SidePanel, cn } from 'ui'
1819
import { Admonition } from 'ui-patterns'
1920

2021
const IPv4SidePanel = () => {
22+
const isAws = useIsAwsCloudProvider()
2123
const { ref: projectRef } = useParams()
2224
const organization = useSelectedOrganization()
2325

@@ -86,7 +88,7 @@ const IPv4SidePanel = () => {
8688
onCancel={closePanel}
8789
onConfirm={onConfirm}
8890
loading={isLoading || isSubmitting}
89-
disabled={isFreePlan || isLoading || !hasChanges || isSubmitting || !canUpdateIPv4}
91+
disabled={isFreePlan || isLoading || !hasChanges || isSubmitting || !canUpdateIPv4 || !isAws}
9092
tooltip={
9193
isFreePlan
9294
? 'Unable to enable IPv4 on a Free Plan'
@@ -117,6 +119,13 @@ const IPv4SidePanel = () => {
117119
database via a IPv4 address.
118120
</p>
119121

122+
{!isAws && (
123+
<Admonition
124+
type="default"
125+
title="Dedicated IPv4 address is only available for AWS projects"
126+
/>
127+
)}
128+
120129
{isPgBouncerEnabled ? (
121130
<Admonition
122131
type="default"
@@ -168,7 +177,7 @@ const IPv4SidePanel = () => {
168177
className="col-span-4 !p-0"
169178
name="ipv4"
170179
key={option.identifier}
171-
disabled={isFreePlan}
180+
disabled={isFreePlan || !isAws}
172181
checked={selectedOption === option.identifier}
173182
label={option.name}
174183
value={option.identifier}

apps/studio/components/interfaces/Settings/General/General.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { PermissionAction } from '@supabase/shared-types/out/constants'
2-
import { AlertCircle, BarChart2, ChevronRight } from 'lucide-react'
2+
import { BarChart2 } from 'lucide-react'
33
import Link from 'next/link'
44
import { toast } from 'sonner'
55

@@ -13,15 +13,11 @@ import { useProjectUpdateMutation } from 'data/projects/project-update-mutation'
1313
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
1414
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
1515
import { useProjectByRef } from 'hooks/misc/useSelectedProject'
16-
import { useFlag } from 'hooks/ui/useFlag'
1716
import {
1817
AlertDescription_Shadcn_,
1918
AlertTitle_Shadcn_,
2019
Alert_Shadcn_,
2120
Button,
22-
CollapsibleContent_Shadcn_,
23-
CollapsibleTrigger_Shadcn_,
24-
Collapsible_Shadcn_,
2521
Form,
2622
Input,
2723
WarningIcon,
@@ -125,7 +121,7 @@ const General = () => {
125121
<div className="mt-6" id="restart-project">
126122
<FormPanel>
127123
<div className="flex flex-col px-8 py-4">
128-
<div className="flex justify-between">
124+
<div className="flex justify-between items-center">
129125
<div>
130126
<p className="text-sm">Restart project</p>
131127
<div className="max-w-[420px]">

apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { useProjectPauseMutation } from 'data/projects/project-pause-mutation'
1414
import { setProjectStatus } from 'data/projects/projects-query'
1515
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
1616
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
17-
import { useIsAwsK8s } from 'hooks/misc/useSelectedProject'
17+
import { useIsAwsK8sCloudProvider } from 'hooks/misc/useSelectedProject'
1818
import { PROJECT_STATUS } from 'lib/constants'
1919
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
2020

@@ -33,7 +33,7 @@ const PauseProjectButton = () => {
3333
'queue_jobs.projects.pause'
3434
)
3535

36-
const isAwsK8s = useIsAwsK8s()
36+
const isAwsK8s = useIsAwsK8sCloudProvider()
3737
const isFreePlan = organization?.plan.id === 'free'
3838
const isPaidAndNotAwsK8s = !isFreePlan && !isAwsK8s
3939

apps/studio/components/interfaces/Settings/General/Infrastructure/RestartServerButton.tsx

Lines changed: 64 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,19 @@ import {
99
useIsProjectActive,
1010
useProjectContext,
1111
} from 'components/layouts/ProjectLayout/ProjectContext'
12+
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
1213
import { useProjectRestartMutation } from 'data/projects/project-restart-mutation'
1314
import { useProjectRestartServicesMutation } from 'data/projects/project-restart-services-mutation'
1415
import { setProjectStatus } from 'data/projects/projects-query'
1516
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
17+
import { useIsAwsK8sCloudProvider } from 'hooks/misc/useSelectedProject'
1618
import { useFlag } from 'hooks/ui/useFlag'
1719
import {
1820
Button,
1921
DropdownMenu,
2022
DropdownMenuContent,
2123
DropdownMenuItem,
2224
DropdownMenuTrigger,
23-
Tooltip,
24-
TooltipContent,
25-
TooltipTrigger,
2625
cn,
2726
} from 'ui'
2827
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
@@ -32,6 +31,7 @@ const RestartServerButton = () => {
3231
const queryClient = useQueryClient()
3332
const { project } = useProjectContext()
3433
const isProjectActive = useIsProjectActive()
34+
const isAwsK8s = useIsAwsK8sCloudProvider()
3535
const [serviceToRestart, setServiceToRestart] = useState<'project' | 'database'>()
3636

3737
const projectRef = project?.ref ?? ''
@@ -88,69 +88,68 @@ const RestartServerButton = () => {
8888

8989
return (
9090
<>
91-
<Tooltip>
92-
<TooltipTrigger asChild>
93-
<div className="flex items-center">
94-
<Button
95-
type="default"
96-
className={cn(
97-
'px-3 hover:z-10',
98-
canRestartProject && isProjectActive ? 'rounded-r-none' : ''
99-
)}
100-
disabled={
101-
project === undefined ||
102-
!canRestartProject ||
103-
!isProjectActive ||
104-
projectRestartDisabled
105-
}
106-
onClick={() => setServiceToRestart('project')}
107-
>
108-
Restart project
109-
</Button>
110-
{canRestartProject && isProjectActive && !projectRestartDisabled && (
111-
<DropdownMenu>
112-
<DropdownMenuTrigger asChild>
113-
<Button
114-
type="default"
115-
className="rounded-l-none px-[4px] py-[5px] -ml-[1px]"
116-
icon={<ChevronDown />}
117-
disabled={!canRestartProject}
118-
/>
119-
</DropdownMenuTrigger>
120-
<DropdownMenuContent align="end" side="bottom">
121-
<DropdownMenuItem
122-
key="database"
123-
disabled={isLoading}
124-
onClick={() => {
125-
setServiceToRestart('database')
126-
}}
127-
>
128-
<div className="space-y-1">
129-
<p className="block text-foreground">Fast database reboot</p>
130-
<p className="block text-foreground-light">
131-
Restarts only the database - faster but may not be able to recover from all
132-
failure modes
133-
</p>
134-
</div>
135-
</DropdownMenuItem>
136-
</DropdownMenuContent>
137-
</DropdownMenu>
138-
)}
139-
</div>
140-
</TooltipTrigger>
141-
{((project !== undefined && (!canRestartProject || !isProjectActive)) ||
142-
projectRestartDisabled) && (
143-
<TooltipContent side="bottom">
144-
{projectRestartDisabled
145-
? 'Project restart is currently disabled'
146-
: !canRestartProject
147-
? 'You need additional permissions to restart this project'
148-
: !isProjectActive
149-
? 'Unable to restart project as project is not active'
150-
: ''}
151-
</TooltipContent>
91+
<div className="flex">
92+
<ButtonTooltip
93+
type="default"
94+
className={cn(
95+
'px-3 hover:z-10',
96+
canRestartProject && isProjectActive ? 'rounded-r-none' : ''
97+
)}
98+
disabled={
99+
project === undefined ||
100+
!canRestartProject ||
101+
!isProjectActive ||
102+
projectRestartDisabled ||
103+
isAwsK8s
104+
}
105+
onClick={() => setServiceToRestart('project')}
106+
tooltip={{
107+
content: {
108+
side: 'bottom',
109+
text: projectRestartDisabled
110+
? 'Project restart is currently disabled'
111+
: !canRestartProject
112+
? 'You need additional permissions to restart this project'
113+
: !isProjectActive
114+
? 'Unable to restart project as project is not active'
115+
: isAwsK8s
116+
? 'Project restart is not supported for AWS (Revamped) projects'
117+
: '',
118+
},
119+
}}
120+
>
121+
Restart project
122+
</ButtonTooltip>
123+
{canRestartProject && isProjectActive && !projectRestartDisabled && (
124+
<DropdownMenu>
125+
<DropdownMenuTrigger asChild>
126+
<Button
127+
type="default"
128+
className="rounded-l-none px-[4px] py-[5px] -ml-[1px]"
129+
icon={<ChevronDown />}
130+
disabled={!canRestartProject}
131+
/>
132+
</DropdownMenuTrigger>
133+
<DropdownMenuContent align="end" side="bottom">
134+
<DropdownMenuItem
135+
key="database"
136+
disabled={isLoading}
137+
onClick={() => {
138+
setServiceToRestart('database')
139+
}}
140+
>
141+
<div className="space-y-1">
142+
<p className="block text-foreground">Fast database reboot</p>
143+
<p className="block text-foreground-light">
144+
Restarts only the database - faster but may not be able to recover from all
145+
failure modes
146+
</p>
147+
</div>
148+
</DropdownMenuItem>
149+
</DropdownMenuContent>
150+
</DropdownMenu>
152151
)}
153-
</Tooltip>
152+
</div>
154153

155154
<ConfirmModal
156155
danger

0 commit comments

Comments
 (0)