mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Joshen/depr 262 bucketid route needs to handle if a bucket doesnt exist (#40430)
* Handle landing on a bucket id that doesnt exist in project * Standardize re routing behaviour for files bucket * refactor --------- Co-authored-by: Alaister Young <a@alaisteryoung.com>
This commit is contained in:
1 parent
7e783dcb0a
commit
8b5a087eb7
9 files changed
+113
-66
No files matched your search
+4
-6
@@ -11,7 +11,6 @@ import {
|
||||
convertKVStringArrayToJson,
|
||||
formatWrapperTables,
|
||||
} from 'components/interfaces/Integrations/Wrappers/Wrappers.utils'
|
||||
import { useSelectedBucket } from 'components/interfaces/Storage/StorageExplorer/useSelectedBucket'
|
||||
import {
|
||||
ScaffoldContainer,
|
||||
ScaffoldSection,
|
||||
@@ -25,7 +24,6 @@ import {
|
||||
} from 'data/database-extensions/database-extensions-query'
|
||||
import { useReplicationPipelineStatusQuery } from 'data/etl/pipeline-status-query'
|
||||
import { useStartPipelineMutation } from 'data/etl/start-pipeline-mutation'
|
||||
import { AnalyticsBucket } from 'data/storage/analytics-buckets-query'
|
||||
import { useIcebergNamespacesQuery } from 'data/storage/iceberg-namespaces-query'
|
||||
import { useIcebergWrapperCreateMutation } from 'data/storage/iceberg-wrapper-create-mutation'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
@@ -34,6 +32,7 @@ import { Button, Card, CardContent } from 'ui'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
import { GenericTableLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
import { DeleteAnalyticsBucketModal } from '../DeleteAnalyticsBucketModal'
|
||||
import { useSelectedAnalyticsBucket } from '../useSelectedAnalyticsBucket'
|
||||
import { BucketHeader } from './BucketHeader'
|
||||
import { ConnectTablesDialog } from './ConnectTablesDialog'
|
||||
import { NamespaceWithTables } from './NamespaceWithTables'
|
||||
@@ -48,12 +47,11 @@ export const AnalyticBucketDetails = () => {
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const { state: extensionState } = useIcebergWrapperExtension()
|
||||
const {
|
||||
bucket: _bucket,
|
||||
data: bucket,
|
||||
error: bucketError,
|
||||
isSuccess: isSuccessBucket,
|
||||
isError: isErrorBucket,
|
||||
} = useSelectedBucket()
|
||||
const bucket = _bucket as undefined | AnalyticsBucket
|
||||
} = useSelectedAnalyticsBucket()
|
||||
|
||||
const [modal, setModal] = useState<'delete' | null>(null)
|
||||
// [Joshen] Namespaces are now created asynchronously when the pipeline is started, so long poll after
|
||||
@@ -332,7 +330,7 @@ export const AnalyticBucketDetails = () => {
|
||||
</div>
|
||||
<Button
|
||||
type="danger"
|
||||
disabled={!isSuccessBucket}
|
||||
disabled={!bucket?.id || !isSuccessBucket}
|
||||
onClick={() => setModal('delete')}
|
||||
>
|
||||
Delete bucket
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { useParams } from 'common'
|
||||
import { useIsAnalyticsBucketsEnabled } from 'data/config/project-storage-config-query'
|
||||
import { useAnalyticsBucketsQuery } from 'data/storage/analytics-buckets-query'
|
||||
|
||||
export const useSelectedAnalyticsBucket = () => {
|
||||
const { ref, bucketId } = useParams()
|
||||
const hasIcebergEnabled = useIsAnalyticsBucketsEnabled({ projectRef: ref })
|
||||
|
||||
return useAnalyticsBucketsQuery(
|
||||
{ projectRef: ref },
|
||||
{
|
||||
enabled: hasIcebergEnabled,
|
||||
select(data) {
|
||||
return data.find((x) => x.id === bucketId)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { useParams } from 'common'
|
||||
import { useBucketsQuery } from 'data/storage/buckets-query'
|
||||
|
||||
export const useSelectedBucket = () => {
|
||||
const { ref, bucketId } = useParams()
|
||||
|
||||
return useBucketsQuery(
|
||||
{ projectRef: ref },
|
||||
{
|
||||
select(data) {
|
||||
return data.find((b) => b.id === bucketId)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import { useParams } from 'common'
|
||||
import { useIsAnalyticsBucketsEnabled } from 'data/config/project-storage-config-query'
|
||||
import { useAnalyticsBucketsQuery } from 'data/storage/analytics-buckets-query'
|
||||
import { useBucketsQuery } from 'data/storage/buckets-query'
|
||||
import { useStorageV2Page } from '../Storage.utils'
|
||||
|
||||
export const useSelectedBucket = () => {
|
||||
const { ref, bucketId } = useParams()
|
||||
const page = useStorageV2Page()
|
||||
const hasIcebergEnabled = useIsAnalyticsBucketsEnabled({ projectRef: ref })
|
||||
|
||||
const {
|
||||
data: analyticsBuckets = [],
|
||||
isSuccess: isSuccessAnalyticsBuckets,
|
||||
isError: isErrorAnalyticsBuckets,
|
||||
error: errorAnalyticsBuckets,
|
||||
} = useAnalyticsBucketsQuery({ projectRef: ref })
|
||||
|
||||
const {
|
||||
data: buckets = [],
|
||||
isSuccess: isSuccessBuckets,
|
||||
isError: isErrorBuckets,
|
||||
error: errorBuckets,
|
||||
} = useBucketsQuery({ projectRef: ref })
|
||||
|
||||
const isSuccess = hasIcebergEnabled
|
||||
? isSuccessBuckets && isSuccessAnalyticsBuckets
|
||||
: isSuccessBuckets
|
||||
const isError = hasIcebergEnabled ? isErrorBuckets || isErrorAnalyticsBuckets : isErrorBuckets
|
||||
const error = hasIcebergEnabled ? errorBuckets || errorAnalyticsBuckets : errorBuckets
|
||||
|
||||
const bucket =
|
||||
page === 'files'
|
||||
? buckets.find((b) => b.id === bucketId)
|
||||
: page === 'analytics'
|
||||
? analyticsBuckets.find((b: any) => b.id === bucketId)
|
||||
: buckets.find((b) => b.id === bucketId)
|
||||
|
||||
return { bucket, isSuccess, isError, error }
|
||||
}
|
||||
@@ -49,10 +49,13 @@ import { DeleteVectorTableModal } from './DeleteVectorTableModal'
|
||||
import { getVectorBucketFDWSchemaName } from './VectorBuckets.utils'
|
||||
import { useS3VectorsWrapperExtension } from './useS3VectorsWrapper'
|
||||
import { useS3VectorsWrapperInstance } from './useS3VectorsWrapperInstance'
|
||||
import { useSelectedVectorBucket } from './useSelectedVectorBuckets'
|
||||
|
||||
export const VectorBucketDetails = () => {
|
||||
const router = useRouter()
|
||||
const { ref: projectRef, bucketId } = useParams()
|
||||
// [Joshen] Use the list buckets to verify that the bucket exists first before fetching bucket details
|
||||
const { data: _bucket, isSuccess } = useSelectedVectorBucket()
|
||||
|
||||
const [filterString, setFilterString] = useState('')
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
@@ -63,7 +66,10 @@ export const VectorBucketDetails = () => {
|
||||
error: bucketError,
|
||||
isSuccess: isSuccessBucket,
|
||||
isError: isErrorBucket,
|
||||
} = useVectorBucketQuery({ projectRef, vectorBucketName: bucketId })
|
||||
} = useVectorBucketQuery(
|
||||
{ projectRef, vectorBucketName: bucketId },
|
||||
{ enabled: isSuccess && !!_bucket }
|
||||
)
|
||||
|
||||
const { data, isLoading: isLoadingIndexes } = useVectorBucketsIndexesQuery({
|
||||
projectRef,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { useParams } from 'common'
|
||||
import { useVectorBucketsQuery } from 'data/storage/vector-buckets-query'
|
||||
|
||||
export const useSelectedVectorBucket = () => {
|
||||
const { ref: projectRef, bucketId } = useParams()
|
||||
|
||||
return useVectorBucketsQuery(
|
||||
{ projectRef },
|
||||
{
|
||||
select(data) {
|
||||
return data.vectorBuckets.find((x) => x.vectorBucketName === bucketId)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { AnalyticBucketDetails } from 'components/interfaces/Storage/AnalyticsBuckets/AnalyticsBucketDetails'
|
||||
import { useSelectedAnalyticsBucket } from 'components/interfaces/Storage/AnalyticsBuckets/useSelectedAnalyticsBucket'
|
||||
import { BUCKET_TYPES } from 'components/interfaces/Storage/Storage.constants'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import { PageLayout } from 'components/layouts/PageLayout/PageLayout'
|
||||
@@ -11,8 +16,18 @@ import type { NextPageWithLayout } from 'types'
|
||||
|
||||
const AnalyticsBucketPage: NextPageWithLayout = () => {
|
||||
const config = BUCKET_TYPES.analytics
|
||||
const { bucketId } = useParams()
|
||||
const router = useRouter()
|
||||
const { ref, bucketId } = useParams()
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const { data: bucket, isSuccess } = useSelectedAnalyticsBucket()
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess && !bucket) {
|
||||
toast.info(`Bucket "${bucketId}" does not exist in your project`)
|
||||
router.push(`/project/${ref}/storage/analytics`)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSuccess])
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { ChevronDown, FolderOpen, Settings, Shield, Trash2 } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { DeleteBucketModal } from 'components/interfaces/Storage/DeleteBucketModal'
|
||||
import { EditBucketModal } from 'components/interfaces/Storage/EditBucketModal'
|
||||
import { EmptyBucketModal } from 'components/interfaces/Storage/EmptyBucketModal'
|
||||
import { useSelectedBucket } from 'components/interfaces/Storage/FilesBuckets/useSelectedBucket'
|
||||
import StorageBucketsError from 'components/interfaces/Storage/StorageBucketsError'
|
||||
import { StorageExplorer } from 'components/interfaces/Storage/StorageExplorer/StorageExplorer'
|
||||
import { useSelectedBucket } from 'components/interfaces/Storage/StorageExplorer/useSelectedBucket'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import { PageLayout } from 'components/layouts/PageLayout/PageLayout'
|
||||
import StorageLayout from 'components/layouts/StorageLayout/StorageLayout'
|
||||
@@ -29,15 +31,24 @@ import {
|
||||
} from 'ui'
|
||||
|
||||
const BucketPage: NextPageWithLayout = () => {
|
||||
const router = useRouter()
|
||||
const { bucketId, ref } = useParams()
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const { projectRef } = useStorageExplorerStateSnapshot()
|
||||
const { bucket, error, isSuccess, isError } = useSelectedBucket()
|
||||
const { data: bucket, error, isSuccess, isError } = useSelectedBucket()
|
||||
const [modal, setModal] = useState<'edit' | 'empty' | 'delete' | null>(null)
|
||||
|
||||
const { getPolicyCount } = useStoragePolicyCounts(bucket ? [bucket as Bucket] : [])
|
||||
const policyCount = bucket ? getPolicyCount(bucket.id) : 0
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess && !bucket) {
|
||||
toast.info(`Bucket "${bucketId}" does not exist in your project`)
|
||||
router.push(`/project/${ref}/storage/files`)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSuccess])
|
||||
|
||||
// [Joshen] Checking against projectRef from storage explorer to check if the store has initialized
|
||||
if (!project || !projectRef || !isSuccess) return null
|
||||
|
||||
@@ -45,15 +56,6 @@ const BucketPage: NextPageWithLayout = () => {
|
||||
return <StorageBucketsError error={error as any} />
|
||||
}
|
||||
|
||||
// If the bucket is not found or the bucket type is ANALYTICS or VECTOR, show an error message
|
||||
if (!bucket || ('type' in bucket && bucket.type !== 'STANDARD')) {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<p className="text-sm text-foreground-light">Bucket "{bucketId}" cannot be found</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageLayout
|
||||
@@ -67,8 +69,8 @@ const BucketPage: NextPageWithLayout = () => {
|
||||
className="[&>div:first-child]:!border-b-0" // Override the border-b from ScaffoldContainer
|
||||
title={
|
||||
<div className="flex items-center gap-2 min-w-0 flex-1">
|
||||
<span className="truncate">{bucket.name}</span>
|
||||
{bucket.public && (
|
||||
<span className="truncate">{bucketId}</span>
|
||||
{bucket?.public && (
|
||||
<Badge variant="warning" size="small" className="flex-shrink-0">
|
||||
Public
|
||||
</Badge>
|
||||
@@ -134,9 +136,11 @@ const BucketPage: NextPageWithLayout = () => {
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="flex-1 min-h-0 px-6 pb-6">
|
||||
<StorageExplorer bucket={bucket} />
|
||||
</div>
|
||||
{!!bucket && (
|
||||
<div className="flex-1 min-h-0 px-6 pb-6">
|
||||
<StorageExplorer bucket={bucket} />
|
||||
</div>
|
||||
)}
|
||||
</PageLayout>
|
||||
|
||||
{bucket && (
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { BUCKET_TYPES } from 'components/interfaces/Storage/Storage.constants'
|
||||
import { useSelectedVectorBucket } from 'components/interfaces/Storage/VectorBuckets/useSelectedVectorBuckets'
|
||||
import { VectorBucketDetails } from 'components/interfaces/Storage/VectorBuckets/VectorBucketDetails'
|
||||
import DefaultLayout from 'components/layouts/DefaultLayout'
|
||||
import { PageLayout } from 'components/layouts/PageLayout/PageLayout'
|
||||
@@ -11,9 +16,20 @@ import type { NextPageWithLayout } from 'types'
|
||||
|
||||
const VectorsBucketPage: NextPageWithLayout = () => {
|
||||
const config = BUCKET_TYPES['vectors']
|
||||
const { bucketId } = useParams()
|
||||
const router = useRouter()
|
||||
const { ref, bucketId } = useParams()
|
||||
const { projectRef } = useStorageExplorerStateSnapshot()
|
||||
|
||||
const { data: bucket, isSuccess } = useSelectedVectorBucket()
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess && !bucket) {
|
||||
toast.info(`Bucket "${bucketId}" does not exist in your project`)
|
||||
router.push(`/project/${ref}/storage/vectors`)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSuccess])
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
title={bucketId}
|
||||
|
||||
Reference in new issue
Block a user