chore(studio): remove Fly.io integration code (#44336)

The Fly login/auth endpoints were removed from the management API
(supabase/platform#30987). This cleans up the associated studio code and
regenerates the API types.

Note: existing Fly projects are still running, so all `cloud_provider`
guards and Fly-specific UI (disk management, billing, pg_cron warnings,
etc.) are intentionally kept in place.

**Removed:**
- `sign-in-fly-tos.tsx` page
- `organization-by-fly-organization-id-mutation.ts`
- `project-by-fly-extension-id-mutation.ts`

**Other:**
- Regenerated API types to reflect removed endpoints
- Removed stale Fly-related comments in `InstanceConfiguration`,
`ObservabilityMenu`, `ReportsMenu`
- Fixed unrelated optional chaining bug in `SSOConfig.tsx`

## To test

- Check project creation flow still works
- Verify `/sign-in-fly-tos` no longer resolves

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-04-01 16:13:38 +08:00
1 parent ae6d4330d0
commit 7cf5df7b3e
11 files changed
+29 -401

No files matched your search

-1
View File
@@ -150,7 +150,6 @@
"pages/project/[ref]/settings/jwt/legacy.tsx": 1,
"pages/project/[ref]/sql/quickstarts.tsx": 1,
"pages/project/[ref]/sql/templates.tsx": 1,
"pages/sign-in-fly-tos.tsx": 1,
"pages/sign-in-mfa.tsx": 1,
"state/role-impersonation-state.tsx": 1,
"state/sidebar-manager-state.tsx": 1,
@@ -164,7 +164,7 @@ export const SSOConfig = () => {
if (ssoConfig) {
form.reset({
enabled: ssoConfig.enabled,
domains: ssoConfig.domains.map((domain) => ({ value: domain })),
domains: ssoConfig.domains?.map((domain) => ({ value: domain })) ?? [],
metadataXmlUrl: ssoConfig.metadata_xml_url,
metadataXmlFile: ssoConfig.metadata_xml_file,
emailMapping: ssoConfig.email_mapping.map((email) => ({ value: email })),
@@ -236,8 +236,6 @@ const InstanceConfigurationUI = ({ diagramOnly = false }: InstanceConfigurationU
isSuccessReplicas && !isLoadingProject ? '' : 'flex items-center justify-center px-28'
}`}
>
{/* Sometimes the read replicas are loaded before the project info and causes read replicas to be shown on Fly deploys.
You can replicate this to going to this page and refresh. This isLoadingProject flag fixes that. */}
{(isLoading || isLoadingProject) && (
<Loader2 className="animate-spin text-foreground-light" />
)}
@@ -33,7 +33,6 @@ const ObservabilityMenu = () => {
const showOverview = useFlag('observabilityOverview')
const { isSupamonitorEnabled } = useSupamonitorStatus()
// b/c fly doesn't support storage
const storageSupported = useIsFeatureEnabled('project_storage:all')
const { can: canCreateCustomReport } = useAsyncCheckPermissions(
@@ -26,7 +26,6 @@ const ReportsMenu = () => {
const { ref, id } = useParams()
const pageKey = (id || router.pathname.split('/')[4]) as string
// b/c fly doesn't support storage
const storageSupported = useIsFeatureEnabled('project_storage:all')
const { can: canCreateCustomReport } = useAsyncCheckPermissions(
@@ -1,52 +0,0 @@
import { useMutation } from '@tanstack/react-query'
import { toast } from 'sonner'
import { get, handleError } from 'data/fetchers'
import type { ResponseError, UseCustomMutationOptions } from 'types'
export type OrganizationByFlyOrgIdVariables = {
flyOrganizationId: string
}
// [Joshen] Should be converted into a query instead of a mutation
// Will need to update sign-in-fly-tos.tsx as well
export async function getOrganizationByFlyOrgId({
flyOrganizationId,
}: OrganizationByFlyOrgIdVariables) {
const { data, error } = await get('/platform/organizations/fly/{fly_organization_id}', {
params: { path: { fly_organization_id: flyOrganizationId } },
})
if (error) handleError(error)
return data as { slug: string }
}
type OrganizationByFlyOrgIdData = Awaited<ReturnType<typeof getOrganizationByFlyOrgId>>
export const useOrganizationByFlyOrgIdMutation = ({
onSuccess,
onError,
...options
}: Omit<
UseCustomMutationOptions<
OrganizationByFlyOrgIdData,
ResponseError,
OrganizationByFlyOrgIdVariables
>,
'mutationFn'
> = {}) => {
return useMutation<OrganizationByFlyOrgIdData, ResponseError, OrganizationByFlyOrgIdVariables>({
mutationFn: (vars) => getOrganizationByFlyOrgId(vars),
async onSuccess(data, variables, context) {
await onSuccess?.(data, variables, context)
},
async onError(data, variables, context) {
if (onError === undefined) {
toast.error(`Failed to get organization: ${data.message}`)
} else {
onError(data, variables, context)
}
},
...options,
})
}
@@ -1,52 +0,0 @@
import { useMutation } from '@tanstack/react-query'
import { toast } from 'sonner'
import { get, handleError } from 'data/fetchers'
import type { ResponseError, UseCustomMutationOptions } from 'types'
export type ProjectByFlyExtensionIdVariables = {
flyExtensionId: string
}
// [Joshen] Should be converted into a query instead of a mutation
// Will need to update sign-in-fly-tos.tsx as well
export async function getProjectByFlyExtensionId({
flyExtensionId,
}: ProjectByFlyExtensionIdVariables) {
const { data, error } = await get('/platform/projects/fly/{fly_extension_id}', {
params: { path: { fly_extension_id: flyExtensionId } },
})
if (error) handleError(error)
return data as { ref: string }
}
type ProjectByFlyExtensionIdData = Awaited<ReturnType<typeof getProjectByFlyExtensionId>>
export const useProjectByFlyExtensionIdMutation = ({
onSuccess,
onError,
...options
}: Omit<
UseCustomMutationOptions<
ProjectByFlyExtensionIdData,
ResponseError,
ProjectByFlyExtensionIdVariables
>,
'mutationFn'
> = {}) => {
return useMutation<ProjectByFlyExtensionIdData, ResponseError, ProjectByFlyExtensionIdVariables>({
mutationFn: (vars) => getProjectByFlyExtensionId(vars),
async onSuccess(data, variables, context) {
await onSuccess?.(data, variables, context)
},
async onError(data, variables, context) {
if (onError === undefined) {
toast.error(`Failed to get project: ${data.message}`)
} else {
onError(data, variables, context)
}
},
...options,
})
}
+1
View File
@@ -96,6 +96,7 @@ const Wizard: NextPageWithLayout = () => {
const projectCreationDisabled = useFlag('disableProjectCreationAndUpdate')
const showPostgresVersionSelector = useFlag('showPostgresVersionSelector')
const cloudProviderEnabled = useFlag('enableFlyCloudProvider')
const isDataApiGrantTogglesEnabled = useDataApiGrantTogglesEnabled()
// Read the raw flag for telemetry — useDataApiGrantTogglesEnabled coerces undefined→false,
// which would record false for users whose flags haven't loaded yet. The raw value preserves
-135
View File
@@ -1,135 +0,0 @@
import { useTheme } from 'next-themes'
import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { useIsLoggedIn } from 'common'
import { useOrganizationByFlyOrgIdMutation } from 'data/organizations/organization-by-fly-organization-id-mutation'
import { useProjectByFlyExtensionIdMutation } from 'data/projects/project-by-fly-extension-id-mutation'
import { API_URL, BASE_PATH } from 'lib/constants'
import { Button } from 'ui'
const SignInFlyTos = () => {
const [loading, setLoading] = useState(true)
const isLoggedIn = useIsLoggedIn()
const router = useRouter()
const {
isReady,
query: { fly_extension_id, fly_organization_id },
} = router
const { resolvedTheme } = useTheme()
const { mutateAsync: getProjectByFlyExtensionId } = useProjectByFlyExtensionIdMutation({
onSuccess: (res) => {
router.replace(`/project/${res.ref}`)
},
onError: () => {
setLoading(false)
},
})
const { mutateAsync: getOrgByFlyOrgId } = useOrganizationByFlyOrgIdMutation({
onSuccess: () => {
router.replace('/organizations')
},
onError: () => {
setLoading(false)
},
})
useEffect(() => {
if (!isReady) {
return
}
if (!isLoggedIn) {
setLoading(false)
return
}
fly_extension_id
? getProjectByFlyExtensionId({ flyExtensionId: fly_extension_id as string })
: fly_organization_id
? getOrgByFlyOrgId({ flyOrganizationId: fly_organization_id as string })
: setLoading(false)
}, [isReady])
const [isRedirecting, setIsRedirecting] = useState(false)
const onSignInWithFly = async () => {
setIsRedirecting(true)
try {
window.location.href = fly_extension_id
? `${API_URL}/tos/fly?extension_id=${fly_extension_id}`
: `${API_URL}/tos/fly?organization_id=${fly_organization_id}`
} catch (error) {
setIsRedirecting(false)
throw error
}
}
return (
<div className="relative mx-auto flex flex-1 w-full flex-col items-center justify-center space-y-6 h-screen">
<div className="absolute top-0 mx-auto w-full max-w-7xl px-8 pt-6 sm:px-6 lg:px-8">
<nav className="relative flex items-center justify-between sm:h-10">
<div className="flex flex-shrink-0 flex-grow items-center lg:flex-grow-0">
<div className="flex w-full items-center justify-between md:w-auto">
<Link href="/projects">
<div>
<Image
src={
resolvedTheme?.includes('dark')
? `${BASE_PATH}/img/supabase-dark.svg`
: `${BASE_PATH}/img/supabase-light.svg`
}
alt=""
height={24}
width={120}
/>
</div>
</Link>
</div>
</div>
</nav>
</div>
<div className="flex w-[320px] flex-col items-center justify-center space-y-3">
<h4 className="text-lg">Continue to Supabase Dashboard</h4>
</div>
{loading ? (
<p className="text-sm">Checking your access rights...</p>
) : (
<div className="flex flex-col items-center space-x-4 space-y-4">
<Button
onClick={onSignInWithFly}
disabled={(!fly_extension_id && !fly_organization_id) || isRedirecting}
loading={isRedirecting}
>
Login with Fly.io
</Button>
{isReady && !fly_extension_id && !fly_organization_id && (
<p className="text-red-900 text-sm">
A fly extension ID or organization ID is required to login
</p>
)}
</div>
)}
<div className="text-center text-balance">
<p className="text-xs text-foreground-lighter sm:mx-auto sm:max-w-sm">
By continuing, you agree to Supabase’s{' '}
<Link href="https://supabase.com/terms" className="underline hover:text-foreground-light">
Terms of Service
</Link>{' '}
and{' '}
<Link
href="https://supabase.com/privacy"
className="underline hover:text-foreground-light"
>
Privacy Policy
</Link>
, and to receive periodic emails with updates.
</p>
</div>
</div>
)
}
export default SignInFlyTos
+6 -6
View File
@@ -1267,22 +1267,22 @@ export interface paths {
cookie?: never
}
/**
* [Beta] List applied migration versions
* List applied migration versions
* @description Only available to selected partner OAuth apps
*/
get: operations['v1-list-migration-history']
/**
* [Beta] Upsert a database migration without applying
* Upsert a database migration without applying
* @description Only available to selected partner OAuth apps
*/
put: operations['v1-upsert-a-migration']
/**
* [Beta] Apply a database migration
* Apply a database migration
* @description Only available to selected partner OAuth apps
*/
post: operations['v1-apply-a-migration']
/**
* [Beta] Rollback database migrations and remove them from history table
* Rollback database migrations and remove them from history table
* @description Only available to selected partner OAuth apps
*/
delete: operations['v1-rollback-migrations']
@@ -1299,7 +1299,7 @@ export interface paths {
cookie?: never
}
/**
* [Beta] Fetch an existing entry from migration history
* Fetch an existing entry from migration history
* @description Only available to selected partner OAuth apps
*/
get: operations['v1-get-a-migration']
@@ -1309,7 +1309,7 @@ export interface paths {
options?: never
head?: never
/**
* [Beta] Patch an existing entry in migration history
* Patch an existing entry in migration history
* @description Only available to selected partner OAuth apps
*/
patch: operations['v1-patch-a-migration']
+21 -150
View File
@@ -1834,23 +1834,6 @@ export interface paths {
patch?: never
trace?: never
}
'/platform/organizations/fly/{fly_organization_id}': {
parameters: {
query?: never
header?: never
path?: never
cookie?: never
}
/** Gets organization linked to fly organization id */
get: operations['OrganizationsController_getOrganizationByFlyOrganizationId']
put?: never
post?: never
delete?: never
options?: never
head?: never
patch?: never
trace?: never
}
'/platform/pg-meta/{ref}/column-privileges': {
parameters: {
query?: never
@@ -3343,23 +3326,6 @@ export interface paths {
patch?: never
trace?: never
}
'/platform/projects/fly/{fly_extension_id}': {
parameters: {
query?: never
header?: never
path?: never
cookie?: never
}
/** Gets project linked to fly extension id */
get: operations['ProjectsController_getProjectByFlyExtensionId']
put?: never
post?: never
delete?: never
options?: never
head?: never
patch?: never
trace?: never
}
'/platform/replication/{ref}/destinations': {
parameters: {
query?: never
@@ -4458,23 +4424,6 @@ export interface paths {
patch?: never
trace?: never
}
'/platform/tos/fly': {
parameters: {
query?: never
header?: never
path?: never
cookie?: never
}
/** Redirects to Fly sso flow */
get: operations['TermsOfServiceController_flyTosAccepted']
put?: never
post?: never
delete?: never
options?: never
head?: never
patch?: never
trace?: never
}
'/platform/update-email': {
parameters: {
query?: never
@@ -5746,7 +5695,8 @@ export interface components {
}
CreateSSOProviderBody:
| {
domains: string[]
/** @default [] */
domains?: string[]
email_mapping: string[]
enabled: boolean
first_name_mapping?: string[]
@@ -5760,7 +5710,8 @@ export interface components {
user_name_mapping?: string[]
}
| {
domains: string[]
/** @default [] */
domains?: string[]
email_mapping: string[]
enabled: boolean
first_name_mapping?: string[]
@@ -5774,7 +5725,8 @@ export interface components {
}
CreateSSOProviderResponse:
| {
domains: string[]
/** @default [] */
domains?: string[]
email_mapping: string[]
enabled: boolean
first_name_mapping?: string[]
@@ -5788,7 +5740,8 @@ export interface components {
user_name_mapping?: string[]
}
| {
domains: string[]
/** @default [] */
domains?: string[]
email_mapping: string[]
enabled: boolean
first_name_mapping?: string[]
@@ -6315,9 +6268,6 @@ export interface components {
}
with_delimiter?: boolean
}
GetOrganizationByFlyOrganizationIdResponse: {
slug: string
}
GetOrganizationIntegrationResponse: {
added_by: {
primary_email: string
@@ -6410,9 +6360,6 @@ export interface components {
| 'DELETING'
}[]
}
GetProjectByFlyExtensionIdResponse: {
ref: string
}
GetProjectLintsResponse: {
cache_key: string
categories: ('PERFORMANCE' | 'SECURITY')[]
@@ -6597,7 +6544,8 @@ export interface components {
}
GetSSOProviderResponse:
| {
domains: string[]
/** @default [] */
domains?: string[]
email_mapping: string[]
enabled: boolean
first_name_mapping?: string[]
@@ -6611,7 +6559,8 @@ export interface components {
user_name_mapping?: string[]
}
| {
domains: string[]
/** @default [] */
domains?: string[]
email_mapping: string[]
enabled: boolean
first_name_mapping?: string[]
@@ -8408,6 +8357,7 @@ export interface components {
connectionString?: string | null
db_host: string
dbVersion?: string
high_availability: boolean
id: number
/** @enum {string} */
infra_compute_size?:
@@ -10754,7 +10704,8 @@ export interface components {
}
UpdateSSOProviderBody:
| {
domains: string[]
/** @default [] */
domains?: string[]
email_mapping: string[]
enabled: boolean
first_name_mapping?: string[]
@@ -10768,7 +10719,8 @@ export interface components {
user_name_mapping?: string[]
}
| {
domains: string[]
/** @default [] */
domains?: string[]
email_mapping: string[]
enabled: boolean
first_name_mapping?: string[]
@@ -10782,7 +10734,8 @@ export interface components {
}
UpdateSSOProviderResponse:
| {
domains: string[]
/** @default [] */
domains?: string[]
email_mapping: string[]
enabled: boolean
first_name_mapping?: string[]
@@ -10796,7 +10749,8 @@ export interface components {
user_name_mapping?: string[]
}
| {
domains: string[]
/** @default [] */
domains?: string[]
email_mapping: string[]
enabled: boolean
first_name_mapping?: string[]
@@ -17068,27 +17022,6 @@ export interface operations {
}
}
}
OrganizationsController_getOrganizationByFlyOrganizationId: {
parameters: {
query?: never
header?: never
path: {
fly_organization_id: string
}
cookie?: never
}
requestBody?: never
responses: {
200: {
headers: {
[name: string]: unknown
}
content: {
'application/json': components['schemas']['GetOrganizationByFlyOrganizationIdResponse']
}
}
}
}
ColumnPrivilegesController_getColumnPrivileges: {
parameters: {
query?: never
@@ -22632,48 +22565,6 @@ export interface operations {
}
}
}
ProjectsController_getProjectByFlyExtensionId: {
parameters: {
query?: never
header?: never
path: {
fly_extension_id: string
}
cookie?: never
}
requestBody?: never
responses: {
200: {
headers: {
[name: string]: unknown
}
content: {
'application/json': components['schemas']['GetProjectByFlyExtensionIdResponse']
}
}
/** @description Unauthorized */
401: {
headers: {
[name: string]: unknown
}
content?: never
}
/** @description Forbidden action */
403: {
headers: {
[name: string]: unknown
}
content?: never
}
/** @description Rate limit exceeded */
429: {
headers: {
[name: string]: unknown
}
content?: never
}
}
}
ReplicationDestinationsController_getDestinations: {
parameters: {
query?: never
@@ -26510,26 +26401,6 @@ export interface operations {
}
}
}
TermsOfServiceController_flyTosAccepted: {
parameters: {
query: {
extension_id: string
organization_id: string
}
header?: never
path?: never
cookie?: never
}
requestBody?: never
responses: {
200: {
headers: {
[name: string]: unknown
}
content?: never
}
}
}
UpdateEmailController_updateEmail: {
parameters: {
query?: never