feat(studio): add Infrastructure replica detail route and redirects (#49045)

## What kind of change does this PR introduce?

Feature. Stack 3 of 5 for
[PIPE-1007](https://linear.app/supabase/issue/PIPE-1007/move-read-replicas-out-of-replication-into-infrastructure).

## What is the current behavior?

Replica detail lives at `/database/replication/replica/:id`.

## What is the new behavior?

Detail moves to `/settings/infrastructure/replica/:id`. Old URLs
redirect. List and diagram View/Manage replica links follow.

## Additional context

Stacked on [#49044](https://github.com/supabase/supabase/pull/49044).
Please review, but do not merge. Merge 2→5 in succession once they are
all reviewed, so users never sit on a split create/list vs detail path.

Replication still lists and creates replicas until
[#49046](https://github.com/supabase/supabase/pull/49046).

## To test

`infrastructure:read_replicas` is an enabled-feature, on by default.
There is no Feature Preview or ConfigCat switch. You should already see
the Infrastructure Read replicas section. If you do not, your profile
lists `infrastructure:read_replicas` in `disabled_features`.

From
[Infrastructure](https://studio-staging-git-danny-pipe-1007-03-detail-redirects-supabase.vercel.app/dashboard/project/_/settings/infrastructure),
open View replica on a row. Confirm you land on
`/settings/infrastructure/replica/:id`. If you have an old bookmark,
`/database/replication/replica/:id` should redirect there.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added read replica management to Infrastructure settings, including
replica creation, status monitoring, details, restart, and removal
actions.
* Added eligibility guidance and estimated pricing details during
replica setup.
* Added support for topology and replica information within
infrastructure configuration.
* **Improvements**
* Legacy database replication links now permanently redirect to the
corresponding Infrastructure pages.
* Added clearer empty, loading, error, and transition states for read
replica management.
* **Tests**
* Expanded coverage for replica navigation, redirects, eligibility
warnings, and empty states.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Danny White authored and GitHub committed 2026-08-21 12:44:41 +10:00
1 parent e9abda6c79
commit 7fd7ace118
12 files changed
+271 -199

No files matched your search

+3 -2
View File
@@ -180,7 +180,7 @@ These are the layout-only TanStack files. Most hold a single product layout comp
- [x] A `routes/project/$ref/database/publications/$id.tsx` ← `pages/project/[ref]/database/publications/[id].tsx`
- [x] A `routes/project/$ref/database/replication/index.tsx` ← `pages/project/[ref]/database/replication/index.tsx`
- [x] A `routes/project/$ref/database/replication/$pipelineId.tsx` ← `pages/project/[ref]/database/replication/[pipelineId].tsx`
- [x] A `routes/project/$ref/database/replication/replica/$replicaId.tsx` ← `pages/project/[ref]/database/replication/replica/[replicaId].tsx`
- [x] A `routes/project/$ref/database/replication/replica/$replicaId.tsx` ← `pages/project/[ref]/database/replication/replica/[replicaId].tsx` (redirects to Infrastructure)
- [x] A `routes/project/$ref/database/triggers/index.tsx` ← `pages/project/[ref]/database/triggers/index.tsx`
- [x] A `routes/project/$ref/database/triggers/data.tsx` ← `pages/project/[ref]/database/triggers/data.tsx` (sub-shell at `database/triggers.tsx` provides PageLayout + nav, parent shell provides DatabaseLayout)
- [x] A `routes/project/$ref/database/triggers/event.tsx` ← `pages/project/[ref]/database/triggers/event.tsx` (same as data)
@@ -296,7 +296,8 @@ These are the layout-only TanStack files. Most hold a single product layout comp
- [x] A `routes/project/$ref/settings/addons.tsx` ← `pages/project/[ref]/settings/addons.tsx`
- [x] A `routes/project/$ref/settings/api.tsx` ← `pages/project/[ref]/settings/api.tsx` (sets `skipSettingsLayout: true` — page is a useEffect redirect)
- [x] A `routes/project/$ref/settings/dashboard.tsx` ← `pages/project/[ref]/settings/dashboard.tsx`
- [x] A `routes/project/$ref/settings/infrastructure.tsx` ← `pages/project/[ref]/settings/infrastructure.tsx`
- [x] A `routes/project/$ref/settings/infrastructure/index.tsx` ← `pages/project/[ref]/settings/infrastructure.tsx`
- [x] A `routes/project/$ref/settings/infrastructure/replica/$replicaId.tsx` ← `pages/project/[ref]/settings/infrastructure/replica/[replicaId].tsx`
- [x] A `routes/project/$ref/settings/integrations.tsx` ← `pages/project/[ref]/settings/integrations.tsx`
- [x] A `routes/project/$ref/settings/log-drains.tsx` ← `pages/project/[ref]/settings/log-drains.tsx`
- [x] A `routes/project/$ref/settings/api-keys/index.tsx` ← `pages/project/[ref]/settings/api-keys/index.tsx` (under `api-keys.tsx` sub-shell with ApiKeysLayout)
@@ -33,6 +33,7 @@ import {
} from './InstanceConfiguration.constants'
import { formatSeconds } from './InstanceConfiguration.utils'
import { metricColor } from './InstanceNode.utils'
import { getReadReplicaPath } from '@/components/interfaces/Settings/Infrastructure/Infrastructure.utils'
import { REPLICA_STATUS } from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/ReadReplicas.constants'
import { SparkBar } from '@/components/ui/SparkBar'
import {
@@ -417,9 +418,7 @@ export const ReplicaNode = ({ data }: NodeProps<Node<ReplicaNodeData>>) => {
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="gap-x-2">
<Link href={`/project/${ref}/database/replication/replica/${id}`}>
Manage replica
</Link>
<Link href={getReadReplicaPath(ref, id)}>Manage replica</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -27,6 +27,7 @@ import { TimestampInfo } from 'ui-patterns/TimestampInfo'
import { AVAILABLE_REPLICA_REGIONS } from './InstanceConfiguration.constants'
import GeographyData from './MapData.json'
import { getReadReplicaPath } from '@/components/interfaces/Settings/Infrastructure/Infrastructure.utils'
import { REPLICA_STATUS } from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/ReadReplicas.constants'
import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
import { formatDatabaseID } from '@/data/read-replicas/replicas.utils'
@@ -303,9 +304,7 @@ const MapView = () => {
<DropdownMenuSeparator />
<DropdownMenuItem className="gap-x-2">
<Link
href={`/project/${ref}/database/replication/replica/${database.identifier}`}
>
<Link href={getReadReplicaPath(ref, database.identifier)}>
Manage replica
</Link>
</DropdownMenuItem>
@@ -24,6 +24,7 @@ import { DropReplicaConfirmationModal } from './DropReplicaConfirmationModal'
import { REPLICA_STATUS } from './ReadReplicas.constants'
import { getIsInTransition, getStatusLabel } from './ReadReplicas.utils'
import { RestartReplicaConfirmationModal } from './RestartReplicaConfirmationModal'
import { getReadReplicaPath } from '@/components/interfaces/Settings/Infrastructure/Infrastructure.utils'
import { useReplicationLagQuery } from '@/data/read-replicas/replica-lag-query'
import { type Database } from '@/data/read-replicas/replicas-query'
import { formatDatabaseID } from '@/data/read-replicas/replicas.utils'
@@ -105,10 +106,6 @@ export const ReadReplicaRow = ({ replica, onUpdateReplica }: ReadReplicaRow) =>
)}
</TableCell>
<TableCell>
<Minus size={18} className="text-foreground-lighter" />
</TableCell>
<TableCell>
<div className="flex items-center justify-end gap-x-2">
<Button
@@ -117,9 +114,7 @@ export const ReadReplicaRow = ({ replica, onUpdateReplica }: ReadReplicaRow) =>
className="relative"
disabled={status === 'GOING_DOWN'}
>
<Link href={`/project/${ref}/database/replication/replica/${replica.identifier}`}>
View replica
</Link>
<Link href={getReadReplicaPath(ref, replica.identifier)}>View replica</Link>
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -1,186 +1,34 @@
import { useParams } from 'common'
import { Database } from 'icons'
import { Loader2, Trash } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useMemo, useState } from 'react'
import { AWS_REGIONS } from 'shared-data'
import { Badge, Button } from 'ui'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
import { useEffect } from 'react'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
import { DropReplicaConfirmationModal } from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/DropReplicaConfirmationModal'
import { ReadReplicaDetails } from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/ReadReplicaDetails'
import { REPLICA_STATUS } from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/ReadReplicas.constants'
import {
getIsInTransition,
getStatusLabel,
} from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/ReadReplicas.utils'
import { RestartReplicaConfirmationModal } from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/RestartReplicaConfirmationModal'
import { getReadReplicaPath } from '@/components/interfaces/Settings/Infrastructure/Infrastructure.utils'
import DatabaseLayout from '@/components/layouts/DatabaseLayout/DatabaseLayout'
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
import { PageLayout } from '@/components/layouts/PageLayout/PageLayout'
import { ScaffoldDescription, ScaffoldTitle } from '@/components/layouts/Scaffold'
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
import CopyButton from '@/components/ui/CopyButton'
import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
import {
ReplicaInitializationStatus,
useReadReplicasStatusesQuery,
} from '@/data/read-replicas/replicas-status-query'
import type { NextPageWithLayout } from '@/types'
const DatabaseReadReplicaPage: NextPageWithLayout = () => {
/** @deprecated Redirects to Settings → Infrastructure replica detail. */
const DatabaseReadReplicaRedirectPage: NextPageWithLayout = () => {
const router = useRouter()
const { ref, replicaId } = useParams()
const [showConfirmRestart, setShowConfirmRestart] = useState(false)
const [showConfirmDrop, setShowConfirmDrop] = useState(false)
const [statusRefetchInterval, setStatusRefetchInterval] = useState<number | false>(5000)
const {
data: databases = [],
isPending: isLoadingDatabases,
isSuccess: isSuccessDatabases,
} = useReadReplicasQuery({ projectRef: ref })
const replica = databases.find((x) => x.identifier === replicaId)
const { identifier, region, status: baseStatus } = replica ?? {}
const { data: statuses = [], isSuccess: isSuccessReplicasStatuses } =
useReadReplicasStatusesQuery({ projectRef: ref }, { refetchInterval: statusRefetchInterval })
const replicaStatus = statuses.find((x) => x.identifier === identifier)
const status = replicaStatus?.status ?? baseStatus
const initStatus = replicaStatus?.replicaInitializationStatus?.status
const regionLabel = Object.values(AWS_REGIONS).find((x) => x.code === region)?.displayName
const statusLabel = useMemo(() => getStatusLabel({ initStatus, status }), [initStatus, status])
const isInTransition = useMemo(
() => getIsInTransition({ initStatus, status }),
[initStatus, status]
)
useEffect(() => {
if (!isSuccessReplicasStatuses) return
const pollReplicas = async () => {
const fixedStatuses = [
REPLICA_STATUS.ACTIVE_HEALTHY,
REPLICA_STATUS.ACTIVE_UNHEALTHY,
REPLICA_STATUS.INIT_READ_REPLICA_FAILED,
]
const replicasInTransition = statuses.filter((db) => {
const { status } = db.replicaInitializationStatus || {}
return (
!fixedStatuses.includes(db.status) || status === ReplicaInitializationStatus.InProgress
)
})
const hasTransientStatus = replicasInTransition.length > 0
// If all replicas are active healthy, stop fetching statuses
if (!hasTransientStatus && statuses.length === databases.length) {
setStatusRefetchInterval(false)
}
}
pollReplicas()
}, [databases.length, isSuccessReplicasStatuses, statuses])
if (!ref || !replicaId) return
router.replace(getReadReplicaPath(ref, replicaId))
}, [ref, replicaId, router])
return (
<PageLayout
title={
<div className="flex items-center gap-x-3">
<ScaffoldTitle>Read Replica</ScaffoldTitle>
{isSuccessDatabases && (
<>
<Badge
variant={
statusLabel === 'Healthy'
? 'success'
: statusLabel === 'Failed'
? 'destructive'
: 'default'
}
>
{statusLabel}
</Badge>
{isInTransition && <Loader2 size={14} className="animate-spin" />}
</>
)}
</div>
}
subtitle={
isLoadingDatabases ? (
<ShimmeringLoader className="py-[11px]" />
) : (
<div className="flex items-center gap-x-2 mt-0!">
<ScaffoldDescription>ID: {identifier}</ScaffoldDescription>
<CopyButton iconOnly variant="default" text={identifier ?? ''} />
</div>
)
}
icon={
<div className="shrink-0 w-10 h-10 relative bg-surface-100 border rounded-md flex items-center justify-center">
<Database size={20} className="text-foreground-light" />
</div>
}
breadcrumbs={[
{
label: 'Replication',
href: `/project/${ref}/database/replication`,
},
{
label: `Read Replica - ${regionLabel}`,
},
]}
secondaryActions={
<ButtonTooltip
variant="default"
className="w-7"
icon={<Trash />}
tooltip={{
content: { side: 'bottom', text: 'Drop replica' },
}}
onClick={() => setShowConfirmDrop(true)}
/>
}
primaryActions={[
<Button asChild key="logs" variant="default">
<Link
href={`/project/${ref}/logs/postgres-logs${!!identifier ? `?db=${identifier}` : ''}`}
>
View logs
</Link>
</Button>,
<Button
key="drop"
variant="default"
disabled={status !== 'ACTIVE_HEALTHY'}
onClick={() => setShowConfirmRestart(true)}
>
Restart replica
</Button>,
]}
>
<ReadReplicaDetails />
<DropReplicaConfirmationModal
selectedReplica={showConfirmDrop ? replica : undefined}
onSuccess={() => router.push(`/project/${ref}/database/replication`)}
onCancel={() => setShowConfirmDrop(false)}
/>
<RestartReplicaConfirmationModal
selectedReplica={showConfirmRestart ? replica : undefined}
onSuccess={() => setStatusRefetchInterval(5000)}
onCancel={() => setShowConfirmRestart(false)}
/>
</PageLayout>
<div className="p-6">
<GenericSkeletonLoader />
</div>
)
}
DatabaseReadReplicaPage.getLayout = (page) => (
DatabaseReadReplicaRedirectPage.getLayout = (page) => (
<DefaultLayout>
<DatabaseLayout title="Replication">{page}</DatabaseLayout>
</DefaultLayout>
)
export default DatabaseReadReplicaPage
export default DatabaseReadReplicaRedirectPage
@@ -0,0 +1,176 @@
import { useParams } from 'common'
import { Database } from 'icons'
import { Loader2, Trash } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useMemo, useState } from 'react'
import { AWS_REGIONS } from 'shared-data'
import { Badge, Button } from 'ui'
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
import { getInfrastructurePath } from '@/components/interfaces/Settings/Infrastructure/Infrastructure.utils'
import { DropReplicaConfirmationModal } from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/DropReplicaConfirmationModal'
import { ReadReplicaDetails } from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/ReadReplicaDetails'
import { REPLICA_STATUS } from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/ReadReplicas.constants'
import {
getIsInTransition,
getStatusLabel,
} from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/ReadReplicas.utils'
import { RestartReplicaConfirmationModal } from '@/components/interfaces/Settings/Infrastructure/ReadReplicas/RestartReplicaConfirmationModal'
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
import { PageLayout } from '@/components/layouts/PageLayout/PageLayout'
import SettingsLayout from '@/components/layouts/ProjectSettingsLayout/SettingsLayout'
import { ScaffoldDescription, ScaffoldTitle } from '@/components/layouts/Scaffold'
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
import CopyButton from '@/components/ui/CopyButton'
import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
import {
ReplicaInitializationStatus,
useReadReplicasStatusesQuery,
} from '@/data/read-replicas/replicas-status-query'
import type { NextPageWithLayout } from '@/types'
const STATUS_BADGE_VARIANT: Record<string, 'success' | 'destructive' | 'default'> = {
Healthy: 'success',
Failed: 'destructive',
}
const InfrastructureReadReplicaPage: NextPageWithLayout = () => {
const router = useRouter()
const { ref, replicaId } = useParams()
const [showConfirmRestart, setShowConfirmRestart] = useState(false)
const [showConfirmDrop, setShowConfirmDrop] = useState(false)
const [statusRefetchInterval, setStatusRefetchInterval] = useState<number | false>(5000)
const {
data: databases = [],
isPending: isLoadingDatabases,
isSuccess: isSuccessDatabases,
} = useReadReplicasQuery({ projectRef: ref })
const replica = databases.find((x) => x.identifier === replicaId)
const { identifier, region, status: baseStatus } = replica ?? {}
const { data: statuses = [], isSuccess: isSuccessReplicasStatuses } =
useReadReplicasStatusesQuery({ projectRef: ref }, { refetchInterval: statusRefetchInterval })
const replicaStatus = statuses.find((x) => x.identifier === identifier)
const status = replicaStatus?.status ?? baseStatus
const initStatus = replicaStatus?.replicaInitializationStatus?.status
const regionLabel = Object.values(AWS_REGIONS).find((x) => x.code === region)?.displayName
const statusLabel = useMemo(() => getStatusLabel({ initStatus, status }), [initStatus, status])
const isInTransition = useMemo(
() => getIsInTransition({ initStatus, status }),
[initStatus, status]
)
useEffect(() => {
if (!isSuccessReplicasStatuses) return
const fixedStatuses = [
REPLICA_STATUS.ACTIVE_HEALTHY,
REPLICA_STATUS.ACTIVE_UNHEALTHY,
REPLICA_STATUS.INIT_READ_REPLICA_FAILED,
]
const replicasInTransition = statuses.filter((db) => {
const { status: init } = db.replicaInitializationStatus || {}
return !fixedStatuses.includes(db.status) || init === ReplicaInitializationStatus.InProgress
})
const hasTransientStatus = replicasInTransition.length > 0
if (!hasTransientStatus && statuses.length === databases.length) {
setStatusRefetchInterval(false)
}
}, [databases.length, isSuccessReplicasStatuses, statuses])
return (
<PageLayout
title={
<div className="flex items-center gap-x-3">
<ScaffoldTitle>Read Replica</ScaffoldTitle>
{isSuccessDatabases && (
<>
<Badge variant={STATUS_BADGE_VARIANT[statusLabel] ?? 'default'}>{statusLabel}</Badge>
{isInTransition && <Loader2 size={14} className="animate-spin" />}
</>
)}
</div>
}
subtitle={
isLoadingDatabases ? (
<ShimmeringLoader className="py-[11px]" />
) : (
<div className="flex items-center gap-x-2 mt-0!">
<ScaffoldDescription>ID: {identifier}</ScaffoldDescription>
<CopyButton iconOnly variant="default" text={identifier ?? ''} />
</div>
)
}
icon={
<div className="shrink-0 w-10 h-10 relative bg-surface-100 border rounded-md flex items-center justify-center">
<Database size={20} className="text-foreground-light" />
</div>
}
breadcrumbs={[
{
label: 'Infrastructure',
href: getInfrastructurePath(ref),
},
{
label: regionLabel ? `Read Replica - ${regionLabel}` : 'Read Replica',
},
]}
secondaryActions={
<ButtonTooltip
variant="default"
className="w-7"
icon={<Trash />}
disabled={!replica}
tooltip={{
content: { side: 'bottom', text: 'Drop replica' },
}}
onClick={() => setShowConfirmDrop(true)}
/>
}
primaryActions={[
<Button asChild key="logs" variant="default">
<Link
href={`/project/${ref}/logs/postgres-logs${!!identifier ? `?db=${identifier}` : ''}`}
>
View logs
</Link>
</Button>,
<Button
key="restart"
variant="default"
disabled={status !== 'ACTIVE_HEALTHY'}
onClick={() => setShowConfirmRestart(true)}
>
Restart replica
</Button>,
]}
>
<ReadReplicaDetails />
<DropReplicaConfirmationModal
selectedReplica={showConfirmDrop ? replica : undefined}
onSuccess={() => router.push(getInfrastructurePath(ref))}
onCancel={() => setShowConfirmDrop(false)}
/>
<RestartReplicaConfirmationModal
selectedReplica={showConfirmRestart ? replica : undefined}
onSuccess={() => setStatusRefetchInterval(5000)}
onCancel={() => setShowConfirmRestart(false)}
/>
</PageLayout>
)
}
InfrastructureReadReplicaPage.getLayout = (page) => (
<DefaultLayout>
<SettingsLayout title="Infrastructure">{page}</SettingsLayout>
</DefaultLayout>
)
export default InfrastructureReadReplicaPage
+13
View File
@@ -66,6 +66,19 @@ describe('matchRedirect query/hash preservation', () => {
})
})
it('redirects legacy replication replica detail to infrastructure', () => {
expect(
matchRedirect({
pathname: '/project/abc/database/replication/replica/replica-1',
search: {},
isPlatform: true,
})
).toEqual({
destination: '/project/abc/settings/infrastructure/replica/replica-1',
permanent: true,
})
})
it('redirects the legacy compute billing panel to the CPU section', () => {
expect(
matchRedirect({
+5
View File
@@ -119,6 +119,11 @@ export const SHARED_REDIRECTS: StudioRedirect[] = [
destination: '/project/:ref/settings/infrastructure',
permanent: true,
},
{
source: '/project/:ref/database/replication/replica/:replicaId',
destination: '/project/:ref/settings/infrastructure/replica/:replicaId',
permanent: true,
},
{
source: '/project/:ref/settings/billing/subscription',
has: [{ type: 'query', key: 'panel', value: 'pitr' }],
+40 -16
View File
@@ -106,7 +106,7 @@ import { Route as ProjectRefSqlExamplesRouteImport } from './routes/project/$ref
import { Route as ProjectRefSqlIdRouteImport } from './routes/project/$ref/sql/$id'
import { Route as ProjectRefSettingsLogDrainsRouteImport } from './routes/project/$ref/settings/log-drains'
import { Route as ProjectRefSettingsIntegrationsRouteImport } from './routes/project/$ref/settings/integrations'
import { Route as ProjectRefSettingsInfrastructureRouteImport } from './routes/project/$ref/settings/infrastructure'
import { Route as ProjectRefSettingsInfrastructureIndexRouteImport } from './routes/project/$ref/settings/infrastructure/index'
import { Route as ProjectRefSettingsGeneralRouteImport } from './routes/project/$ref/settings/general'
import { Route as ProjectRefSettingsDashboardRouteImport } from './routes/project/$ref/settings/dashboard'
import { Route as ProjectRefSettingsApiKeysRouteImport } from './routes/project/$ref/settings/api-keys'
@@ -285,6 +285,7 @@ import { Route as ApiPlatformAuthRefUsersIndexRouteImport } from './routes/api/p
import { Route as ProjectRefStorageVectorsBucketsBucketIdRouteImport } from './routes/project/$ref/storage/vectors/buckets/$bucketId'
import { Route as ProjectRefStorageFilesBucketsBucketIdRouteImport } from './routes/project/$ref/storage/files/buckets/$bucketId'
import { Route as ProjectRefStorageAnalyticsBucketsBucketIdRouteImport } from './routes/project/$ref/storage/analytics/buckets/$bucketId'
import { Route as ProjectRefSettingsInfrastructureReplicaReplicaIdRouteImport } from './routes/project/$ref/settings/infrastructure/replica/$replicaId'
import { Route as ProjectRefDatabaseReplicationReplicaReplicaIdRouteImport } from './routes/project/$ref/database/replication/replica/$replicaId'
import { Route as ApiV1ProjectsRefTypesTypescriptRouteImport } from './routes/api/v1/projects/$ref/types/typescript'
import { Route as ApiV1ProjectsRefDatabaseMigrationsRouteImport } from './routes/api/v1/projects/$ref/database/migrations'
@@ -823,10 +824,10 @@ const ProjectRefSettingsIntegrationsRoute =
path: '/integrations',
getParentRoute: () => ProjectRefSettingsRoute,
} as any)
const ProjectRefSettingsInfrastructureRoute =
ProjectRefSettingsInfrastructureRouteImport.update({
id: '/infrastructure',
path: '/infrastructure',
const ProjectRefSettingsInfrastructureIndexRoute =
ProjectRefSettingsInfrastructureIndexRouteImport.update({
id: '/infrastructure/',
path: '/infrastructure/',
getParentRoute: () => ProjectRefSettingsRoute,
} as any)
const ProjectRefSettingsGeneralRoute =
@@ -1851,6 +1852,12 @@ const ProjectRefStorageAnalyticsBucketsBucketIdRoute =
path: '/analytics/buckets/$bucketId',
getParentRoute: () => ProjectRefStorageRoute,
} as any)
const ProjectRefSettingsInfrastructureReplicaReplicaIdRoute =
ProjectRefSettingsInfrastructureReplicaReplicaIdRouteImport.update({
id: '/infrastructure/replica/$replicaId',
path: '/infrastructure/replica/$replicaId',
getParentRoute: () => ProjectRefSettingsRoute,
} as any)
const ProjectRefDatabaseReplicationReplicaReplicaIdRoute =
ProjectRefDatabaseReplicationReplicaReplicaIdRouteImport.update({
id: '/replication/replica/$replicaId',
@@ -2249,7 +2256,7 @@ export interface FileRoutesByFullPath {
'/project/$ref/settings/api-keys': typeof ProjectRefSettingsApiKeysRouteWithChildren
'/project/$ref/settings/dashboard': typeof ProjectRefSettingsDashboardRoute
'/project/$ref/settings/general': typeof ProjectRefSettingsGeneralRoute
'/project/$ref/settings/infrastructure': typeof ProjectRefSettingsInfrastructureRoute
'/project/$ref/settings/infrastructure/': typeof ProjectRefSettingsInfrastructureIndexRoute
'/project/$ref/settings/integrations': typeof ProjectRefSettingsIntegrationsRoute
'/project/$ref/settings/log-drains': typeof ProjectRefSettingsLogDrainsRoute
'/project/$ref/sql/$id': typeof ProjectRefSqlIdRoute
@@ -2356,6 +2363,7 @@ export interface FileRoutesByFullPath {
'/api/v1/projects/$ref/database/migrations': typeof ApiV1ProjectsRefDatabaseMigrationsRoute
'/api/v1/projects/$ref/types/typescript': typeof ApiV1ProjectsRefTypesTypescriptRoute
'/project/$ref/database/replication/replica/$replicaId': typeof ProjectRefDatabaseReplicationReplicaReplicaIdRoute
'/project/$ref/settings/infrastructure/replica/$replicaId': typeof ProjectRefSettingsInfrastructureReplicaReplicaIdRoute
'/project/$ref/storage/analytics/buckets/$bucketId': typeof ProjectRefStorageAnalyticsBucketsBucketIdRoute
'/project/$ref/storage/files/buckets/$bucketId': typeof ProjectRefStorageFilesBucketsBucketIdRoute
'/project/$ref/storage/vectors/buckets/$bucketId': typeof ProjectRefStorageVectorsBucketsBucketIdRoute
@@ -2549,7 +2557,7 @@ export interface FileRoutesByTo {
'/project/$ref/settings/api': typeof ProjectRefSettingsApiRoute
'/project/$ref/settings/dashboard': typeof ProjectRefSettingsDashboardRoute
'/project/$ref/settings/general': typeof ProjectRefSettingsGeneralRoute
'/project/$ref/settings/infrastructure': typeof ProjectRefSettingsInfrastructureRoute
'/project/$ref/settings/infrastructure': typeof ProjectRefSettingsInfrastructureIndexRoute
'/project/$ref/settings/integrations': typeof ProjectRefSettingsIntegrationsRoute
'/project/$ref/settings/log-drains': typeof ProjectRefSettingsLogDrainsRoute
'/project/$ref/sql/$id': typeof ProjectRefSqlIdRoute
@@ -2656,6 +2664,7 @@ export interface FileRoutesByTo {
'/api/v1/projects/$ref/database/migrations': typeof ApiV1ProjectsRefDatabaseMigrationsRoute
'/api/v1/projects/$ref/types/typescript': typeof ApiV1ProjectsRefTypesTypescriptRoute
'/project/$ref/database/replication/replica/$replicaId': typeof ProjectRefDatabaseReplicationReplicaReplicaIdRoute
'/project/$ref/settings/infrastructure/replica/$replicaId': typeof ProjectRefSettingsInfrastructureReplicaReplicaIdRoute
'/project/$ref/storage/analytics/buckets/$bucketId': typeof ProjectRefStorageAnalyticsBucketsBucketIdRoute
'/project/$ref/storage/files/buckets/$bucketId': typeof ProjectRefStorageFilesBucketsBucketIdRoute
'/project/$ref/storage/vectors/buckets/$bucketId': typeof ProjectRefStorageVectorsBucketsBucketIdRoute
@@ -2866,7 +2875,7 @@ export interface FileRoutesById {
'/project/$ref/settings/api-keys': typeof ProjectRefSettingsApiKeysRouteWithChildren
'/project/$ref/settings/dashboard': typeof ProjectRefSettingsDashboardRoute
'/project/$ref/settings/general': typeof ProjectRefSettingsGeneralRoute
'/project/$ref/settings/infrastructure': typeof ProjectRefSettingsInfrastructureRoute
'/project/$ref/settings/infrastructure/': typeof ProjectRefSettingsInfrastructureIndexRoute
'/project/$ref/settings/integrations': typeof ProjectRefSettingsIntegrationsRoute
'/project/$ref/settings/log-drains': typeof ProjectRefSettingsLogDrainsRoute
'/project/$ref/sql/$id': typeof ProjectRefSqlIdRoute
@@ -2973,6 +2982,7 @@ export interface FileRoutesById {
'/api/v1/projects/$ref/database/migrations': typeof ApiV1ProjectsRefDatabaseMigrationsRoute
'/api/v1/projects/$ref/types/typescript': typeof ApiV1ProjectsRefTypesTypescriptRoute
'/project/$ref/database/replication/replica/$replicaId': typeof ProjectRefDatabaseReplicationReplicaReplicaIdRoute
'/project/$ref/settings/infrastructure/replica/$replicaId': typeof ProjectRefSettingsInfrastructureReplicaReplicaIdRoute
'/project/$ref/storage/analytics/buckets/$bucketId': typeof ProjectRefStorageAnalyticsBucketsBucketIdRoute
'/project/$ref/storage/files/buckets/$bucketId': typeof ProjectRefStorageFilesBucketsBucketIdRoute
'/project/$ref/storage/vectors/buckets/$bucketId': typeof ProjectRefStorageVectorsBucketsBucketIdRoute
@@ -3182,7 +3192,7 @@ export interface FileRouteTypes {
| '/project/$ref/settings/api-keys'
| '/project/$ref/settings/dashboard'
| '/project/$ref/settings/general'
| '/project/$ref/settings/infrastructure'
| '/project/$ref/settings/infrastructure/'
| '/project/$ref/settings/integrations'
| '/project/$ref/settings/log-drains'
| '/project/$ref/sql/$id'
@@ -3289,6 +3299,7 @@ export interface FileRouteTypes {
| '/api/v1/projects/$ref/database/migrations'
| '/api/v1/projects/$ref/types/typescript'
| '/project/$ref/database/replication/replica/$replicaId'
| '/project/$ref/settings/infrastructure/replica/$replicaId'
| '/project/$ref/storage/analytics/buckets/$bucketId'
| '/project/$ref/storage/files/buckets/$bucketId'
| '/project/$ref/storage/vectors/buckets/$bucketId'
@@ -3589,6 +3600,7 @@ export interface FileRouteTypes {
| '/api/v1/projects/$ref/database/migrations'
| '/api/v1/projects/$ref/types/typescript'
| '/project/$ref/database/replication/replica/$replicaId'
| '/project/$ref/settings/infrastructure/replica/$replicaId'
| '/project/$ref/storage/analytics/buckets/$bucketId'
| '/project/$ref/storage/files/buckets/$bucketId'
| '/project/$ref/storage/vectors/buckets/$bucketId'
@@ -3798,7 +3810,7 @@ export interface FileRouteTypes {
| '/project/$ref/settings/api-keys'
| '/project/$ref/settings/dashboard'
| '/project/$ref/settings/general'
| '/project/$ref/settings/infrastructure'
| '/project/$ref/settings/infrastructure/'
| '/project/$ref/settings/integrations'
| '/project/$ref/settings/log-drains'
| '/project/$ref/sql/$id'
@@ -3905,6 +3917,7 @@ export interface FileRouteTypes {
| '/api/v1/projects/$ref/database/migrations'
| '/api/v1/projects/$ref/types/typescript'
| '/project/$ref/database/replication/replica/$replicaId'
| '/project/$ref/settings/infrastructure/replica/$replicaId'
| '/project/$ref/storage/analytics/buckets/$bucketId'
| '/project/$ref/storage/files/buckets/$bucketId'
| '/project/$ref/storage/vectors/buckets/$bucketId'
@@ -4748,11 +4761,11 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ProjectRefSettingsIntegrationsRouteImport
parentRoute: typeof ProjectRefSettingsRoute
}
'/project/$ref/settings/infrastructure': {
id: '/project/$ref/settings/infrastructure'
'/project/$ref/settings/infrastructure/': {
id: '/project/$ref/settings/infrastructure/'
path: '/infrastructure'
fullPath: '/project/$ref/settings/infrastructure'
preLoaderRoute: typeof ProjectRefSettingsInfrastructureRouteImport
fullPath: '/project/$ref/settings/infrastructure/'
preLoaderRoute: typeof ProjectRefSettingsInfrastructureIndexRouteImport
parentRoute: typeof ProjectRefSettingsRoute
}
'/project/$ref/settings/general': {
@@ -6001,6 +6014,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ProjectRefStorageAnalyticsBucketsBucketIdRouteImport
parentRoute: typeof ProjectRefStorageRoute
}
'/project/$ref/settings/infrastructure/replica/$replicaId': {
id: '/project/$ref/settings/infrastructure/replica/$replicaId'
path: '/infrastructure/replica/$replicaId'
fullPath: '/project/$ref/settings/infrastructure/replica/$replicaId'
preLoaderRoute: typeof ProjectRefSettingsInfrastructureReplicaReplicaIdRouteImport
parentRoute: typeof ProjectRefSettingsRoute
}
'/project/$ref/database/replication/replica/$replicaId': {
id: '/project/$ref/database/replication/replica/$replicaId'
path: '/replication/replica/$replicaId'
@@ -6802,7 +6822,8 @@ interface ProjectRefSettingsRouteChildren {
ProjectRefSettingsApiKeysRoute: typeof ProjectRefSettingsApiKeysRouteWithChildren
ProjectRefSettingsDashboardRoute: typeof ProjectRefSettingsDashboardRoute
ProjectRefSettingsGeneralRoute: typeof ProjectRefSettingsGeneralRoute
ProjectRefSettingsInfrastructureRoute: typeof ProjectRefSettingsInfrastructureRoute
ProjectRefSettingsInfrastructureIndexRoute: typeof ProjectRefSettingsInfrastructureIndexRoute
ProjectRefSettingsInfrastructureReplicaReplicaIdRoute: typeof ProjectRefSettingsInfrastructureReplicaReplicaIdRoute
ProjectRefSettingsIntegrationsRoute: typeof ProjectRefSettingsIntegrationsRoute
ProjectRefSettingsLogDrainsRoute: typeof ProjectRefSettingsLogDrainsRoute
ProjectRefSettingsBillingUsageRoute: typeof ProjectRefSettingsBillingUsageRoute
@@ -6818,7 +6839,10 @@ const ProjectRefSettingsRouteChildren: ProjectRefSettingsRouteChildren = {
ProjectRefSettingsApiKeysRoute: ProjectRefSettingsApiKeysRouteWithChildren,
ProjectRefSettingsDashboardRoute: ProjectRefSettingsDashboardRoute,
ProjectRefSettingsGeneralRoute: ProjectRefSettingsGeneralRoute,
ProjectRefSettingsInfrastructureRoute: ProjectRefSettingsInfrastructureRoute,
ProjectRefSettingsInfrastructureIndexRoute:
ProjectRefSettingsInfrastructureIndexRoute,
ProjectRefSettingsInfrastructureReplicaReplicaIdRoute:
ProjectRefSettingsInfrastructureReplicaReplicaIdRoute,
ProjectRefSettingsIntegrationsRoute: ProjectRefSettingsIntegrationsRoute,
ProjectRefSettingsLogDrainsRoute: ProjectRefSettingsLogDrainsRoute,
ProjectRefSettingsBillingUsageRoute: ProjectRefSettingsBillingUsageRoute,
@@ -1,6 +1,6 @@
import { createFileRoute } from '@tanstack/react-router'
import DatabaseReadReplicaPage from '@/pages/project/[ref]/database/replication/replica/[replicaId]'
import DatabaseReadReplicaRedirectPage from '@/pages/project/[ref]/database/replication/replica/[replicaId]'
export const Route = createFileRoute('/project/$ref/database/replication/replica/$replicaId')({
component: DatabaseReplicationReplicaRoute,
@@ -10,5 +10,5 @@ export const Route = createFileRoute('/project/$ref/database/replication/replica
})
function DatabaseReplicationReplicaRoute() {
return <DatabaseReadReplicaPage dehydratedState={undefined} />
return <DatabaseReadReplicaRedirectPage dehydratedState={undefined} />
}
@@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router'
import ProjectInfrastructure from '@/pages/project/[ref]/settings/infrastructure'
export const Route = createFileRoute('/project/$ref/settings/infrastructure')({
export const Route = createFileRoute('/project/$ref/settings/infrastructure/')({
component: SettingsInfrastructureRoute,
staticData: { settingsLayoutTitle: 'Infrastructure' },
})
@@ -0,0 +1,12 @@
import { createFileRoute } from '@tanstack/react-router'
import InfrastructureReadReplicaPage from '@/pages/project/[ref]/settings/infrastructure/replica/[replicaId]'
export const Route = createFileRoute('/project/$ref/settings/infrastructure/replica/$replicaId')({
component: InfrastructureReadReplicaRoute,
staticData: { settingsLayoutTitle: 'Infrastructure' },
})
function InfrastructureReadReplicaRoute() {
return <InfrastructureReadReplicaPage dehydratedState={undefined} />
}