Files
supabase/apps/studio/components/interfaces/ProjectCreation/ProjectCreation.utils.ts
T
Joshen Lim 0d3b73794b Joshenlim/fe 4465 experiment with best available region selection (#50851)
## Context

Adds a "Best available region" option in the region selector for the
project creation form
- Should only show up for free plan organizations (will be selected as
the default option instead of the recommended option from GET
`/available-regions`)
- "Recommended" badges will also be hidden in this scenario
- Behaviour should be status quo for non free plan organizations
<img width="500" alt="image"
src="https://github.com/user-attachments/assets/de0a183d-37c0-445d-98ca-c5aaf6353e73"
/>

## To test
Important to ensure that project creation still behaves as per usual
- [ ] Free plan: Creating a project with "best available region" select
creates the project if the recommended region from GET
`/available-regions`
- A quick way to check this is to swap to a paid org and see the
"recommended" general region
- [ ] Free plan: Can also create a project with other regions selected
as per usual
- [ ] Non free plan: Can create project as per usual
- [ ] Verify that everything is status quo if configcat feature flag is
off
2026-09-24 17:38:58 +08:00

126 lines
4.3 KiB
TypeScript

import type { CloudProvider, Region } from 'shared-data'
import { AWS_REGIONS } from 'shared-data'
import { SMART_REGION_TO_EXACT_REGION_MAP } from 'shared-data/regions'
import { DesiredInstanceSize, instanceSizeSpecs } from '@/data/projects/new-project.constants'
export function smartRegionToExactRegion(smartOrExactRegion: string) {
return SMART_REGION_TO_EXACT_REGION_MAP.get(smartOrExactRegion) ?? smartOrExactRegion
}
export function getAvailableRegions(
cloudProvider: CloudProvider,
environment = process.env.NEXT_PUBLIC_ENVIRONMENT
): Region {
switch (cloudProvider) {
case 'AWS':
case 'AWS_K8S':
return AWS_REGIONS
case 'AWS_NIMBUS':
if (environment !== 'prod') {
// Only allow Southeast Asia for Nimbus (local/staging)
return {
SOUTHEAST_ASIA: AWS_REGIONS.SOUTHEAST_ASIA,
}
}
// Only allow US East for Nimbus (prod)
return {
EAST_US: AWS_REGIONS.EAST_US,
}
default:
throw new Error('Invalid cloud provider')
}
}
type ResolveDefaultDbRegionArgs = {
cloudProvider: CloudProvider
isHighAvailabilityRestricted: boolean
highAvailabilityRegionName: string | undefined
isSmartRegionEnabled: boolean
recommendedSmartRegion: string | undefined
autoDefaultRegion: string | undefined
fixedDefaultRegion: string
environment?: string
}
export function resolveDefaultDbRegion({
cloudProvider,
isHighAvailabilityRestricted,
highAvailabilityRegionName,
isSmartRegionEnabled,
recommendedSmartRegion,
autoDefaultRegion,
fixedDefaultRegion,
environment = process.env.NEXT_PUBLIC_ENVIRONMENT,
}: ResolveDefaultDbRegionArgs): string | undefined {
if (isHighAvailabilityRestricted) return highAvailabilityRegionName
if (isSmartRegionEnabled) return recommendedSmartRegion
// The geolocated default is only usable if the provider actually offers that region
// (e.g. AWS_NIMBUS is restricted to a single region)
const isAutoDefaultRegionAvailable = Object.entries(
getAvailableRegions(cloudProvider, environment)
).some(([, region]) => region.displayName === autoDefaultRegion)
return isAutoDefaultRegionAvailable && autoDefaultRegion !== undefined
? autoDefaultRegion
: fixedDefaultRegion
}
type ResolveSelectedRegionOptionTypeArgs = {
isBestAvailableSelected: boolean
dbRegion: string | undefined
smartGroupRegions: Array<{ name: string }>
specificRegions: Array<{ name: string }>
}
export function resolveSelectedRegionOptionType({
isBestAvailableSelected,
dbRegion,
smartGroupRegions,
specificRegions,
}: ResolveSelectedRegionOptionTypeArgs): 'general' | 'specific' | undefined {
// The "Best available region" shortcut always resolves to a smart-group recommendation
if (isBestAvailableSelected) return 'general'
if (smartGroupRegions.some((region) => region.name === dbRegion)) return 'general'
if (specificRegions.some((region) => region.name === dbRegion)) return 'specific'
return undefined
}
/**
* When launching new projects, they only get assigned a compute size once successfully launched,
* this might assume wrong compute size, but only for projects being rapidly launched after one another on non-default compute sizes.
*
* Needs to be in the API in the future [kevin]
*/
export const monthlyInstancePrice = (instance: string | undefined): number => {
return instanceSizeSpecs[instance as DesiredInstanceSize]?.priceMonthly || 10
}
export const instanceLabel = (instance: string | undefined): string => {
return instanceSizeSpecs[instance as DesiredInstanceSize]?.label || 'Micro'
}
export const getHighAvailabilityRegionCode = (
environment = process.env.NEXT_PUBLIC_ENVIRONMENT
) => {
// Local dev stacks can run in any of the supported regions, so they're left unrestricted
if (environment === 'prod') return 'us-east-1'
if (environment === 'staging') return 'us-east-1'
if (environment === 'local') return 'eu-central-1'
return undefined
}
export const filterHighAvailabilityRegions = <T extends { code: string }>(
regions: T[],
highAvailability: boolean,
environment = process.env.NEXT_PUBLIC_ENVIRONMENT
) => {
const isLocal = environment === 'local'
const regionCode = getHighAvailabilityRegionCode(environment)
return highAvailability && !isLocal && regionCode !== undefined
? regions.filter((region) => region.code === regionCode)
: regions
}