Files
supabase/apps/studio/data/storage/versioning/archived-objects-query.ts
T
Francesco SansalvadoreandClaude Sonnet 5 44ff5525dc feat(storage): add archived objects data layer
On a versioned bucket a delete is a soft delete, and the file preview panel
already calls that action Archive and promises the versions stay recoverable.
Nothing in the dashboard lets a user see or restore an archived file yet. This is
the data layer for that, with no UI: query and mutation shapes written to the
studio conventions, endpoints stubbed, returning empty.

- `archived-objects-query.ts` — `ArchivedObject` / `ArchivedObjectVersion` types
  and `archivedObjectsQueryOptions`
- `archived-object-restore-mutation.ts` — bring an archived object back
- `archived-object-purge-mutation.ts` — delete it and every version, permanently
- `archived-object-version-delete-mutation.ts` — remove one retained version
- `archivedOverlay.utils.ts` — synthesizes the explorer rows for one folder from
  the archived list
- `archivedVersions.utils.ts` — an archived object's history as one flat list

Two prototype problems fixed rather than carried over:

The prototype identified the "was current when archived" row by the sentinel
`versionId === objectId`, which was load-bearing across three files and would
break the moment real version ids arrived. `ArchivedObject` now carries a
`currentVersion` record, so merging invents no fields and the distinction is an
explicit `wasCurrentAtArchive` flag.

The prototype's object had both `name` and `originalPath`, inconsistently — the
path normalization existed mostly to strip a duplicated leaf folder that
inconsistency produced. There is now a single `path`, and `getArchivedSegments`
is the only function that interprets it, so a different API shape is a one-place
change.

Also drops `deletedBy` and `expiresAt`, which the prototype never rendered.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-07 16:38:46 +02:00

56 lines
1.8 KiB
TypeScript

import { queryOptions } from '@tanstack/react-query'
import { storageKeys } from '../keys'
import type { ObjectVersionAction } from './object-versions-query'
import { IS_PLATFORM } from '@/lib/constants'
import type { ResponseError } from '@/types'
export interface ArchivedObjectVersion {
versionId: string
size: number
createdAt: string
action: ObjectVersionAction
}
export interface ArchivedObject {
id: string
/** Full path including the file name. Only `getArchivedSegments` splits it. */
path: string
archivedAt: string
/**
* The version that was live when the object was archived. S3 puts a delete
* marker on top of the stack; this UI hides that and surfaces the version
* underneath as an ordinary history entry.
*/
currentVersion: ArchivedObjectVersion
/** Newest first. */
noncurrentVersions: ArchivedObjectVersion[]
}
export type ArchivedObjectsVariables = {
projectRef?: string
bucketId?: string
}
export type ArchivedObjectsError = ResponseError
async function getArchivedObjects(
{ projectRef, bucketId }: ArchivedObjectsVariables,
_signal?: AbortSignal
): Promise<ArchivedObject[]> {
if (!projectRef) throw new Error('projectRef is required')
if (!bucketId) throw new Error('bucketId is required')
// TODO(storage-versioning): real endpoint once Storage exposes it.
return []
}
export type ArchivedObjectsData = Awaited<ReturnType<typeof getArchivedObjects>>
export const archivedObjectsQueryOptions = ({ projectRef, bucketId }: ArchivedObjectsVariables) =>
queryOptions({
queryKey: storageKeys.archivedObjects(projectRef, bucketId),
queryFn: ({ signal }) => getArchivedObjects({ projectRef, bucketId }, signal),
enabled: IS_PLATFORM && typeof projectRef !== 'undefined' && typeof bucketId !== 'undefined',
})