mirror of
https://github.com/supabase/supabase.git
synced 2026-10-14 05:39:11 +03:00
Fixes an incoherence in the prototype: the bucket modal had a per-bucket 'Keep
snapshots for N days'. A restore point's value is being consistent across every
bucket the database references, so per-bucket retention lets older restore points
silently degrade into partial ones — restorable for one bucket, gone for another,
discovered mid-incident.
Reassigns each knob to the level where its consequences live:
- Project: frequency + retention + include-new-buckets + per-bucket participation
- Bucket: versioning (with an explicit override) + participation only
Adds frequency as the lever the PRFAQ discussion was missing, defaulted to 'with
every database backup' — an independent cadence would produce storage restore
points with no matching backup, so you could restore files but not the database
state that referenced them. Daily/hourly remain as advanced options.
Also:
- New canonical editor on Storage > Files > Settings (RestorePointsSettings),
replacing the duplicate dialog on the backups page so the two can't drift; the
coverage banner now links there instead of editing the same state separately
- Bucket modal shows the effective versioning retention and its origin
('project default' vs 'overridden for this bucket') so inheritance never has to
be resolved mentally, plus the inherited frequency/retention read-only
- Renames the concept from lifecycle/sync settings to 'restore points'
- Design doc records the reasoning, including why current-object expiry is
deliberately out of scope (different product; nasty interaction with
versioning that would defeat its own cost purpose)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
70 lines
2.5 KiB
TypeScript
70 lines
2.5 KiB
TypeScript
/**
|
|
* Shared constants + gating for the Storage Snapshots & Versioning prototype.
|
|
*/
|
|
|
|
// Prototype flag: force-enabled so the surfaces are visible via `pnpm dev:studio`.
|
|
// A real rollout would replace this with `useFlag('storageObjectVersioning')`.
|
|
export const STORAGE_PROTECTION_ENABLED = true
|
|
|
|
export const useIsStorageProtectionEnabled = () => STORAGE_PROTECTION_ENABLED
|
|
|
|
export type BucketVersioningState = 'enabled' | 'suspended' | 'disabled'
|
|
|
|
/**
|
|
* Project-wide defaults for object versioning. New buckets inherit these so
|
|
* enabling versioning doesn't require re-deciding retention every time; a bucket
|
|
* can still override them when its churn genuinely differs.
|
|
*/
|
|
export const PROJECT_VERSIONING_DEFAULTS = {
|
|
versionExpiryDays: 30,
|
|
maxNoncurrentVersions: 100,
|
|
} as const
|
|
|
|
export interface BucketProtection {
|
|
versioning: BucketVersioningState
|
|
/**
|
|
* Whether this bucket is captured in the project's restore points. Frequency
|
|
* and retention are project-level — see RestorePointPolicy.
|
|
*/
|
|
isIncludedInRestorePoints: boolean
|
|
/** Expire noncurrent versions after N days (null = keep indefinitely). */
|
|
versionExpiryDays: number | null
|
|
/** Cap on retained newer noncurrent versions (max 100). */
|
|
maxNoncurrentVersions: number | null
|
|
/** True when this bucket's version retention differs from the project default. */
|
|
hasVersioningOverride: boolean
|
|
}
|
|
|
|
const PROTECTED_BUCKETS: Record<string, BucketProtection> = {
|
|
'match-media': {
|
|
versioning: 'enabled',
|
|
isIncludedInRestorePoints: true,
|
|
versionExpiryDays: PROJECT_VERSIONING_DEFAULTS.versionExpiryDays,
|
|
maxNoncurrentVersions: PROJECT_VERSIONING_DEFAULTS.maxNoncurrentVersions,
|
|
hasVersioningOverride: false,
|
|
},
|
|
avatars: {
|
|
versioning: 'enabled',
|
|
isIncludedInRestorePoints: true,
|
|
// Higher-churn bucket that deliberately keeps fewer versions than the default
|
|
versionExpiryDays: 30,
|
|
maxNoncurrentVersions: 10,
|
|
hasVersioningOverride: true,
|
|
},
|
|
}
|
|
|
|
const DEFAULT_PROTECTION: BucketProtection = {
|
|
versioning: 'disabled',
|
|
isIncludedInRestorePoints: false,
|
|
versionExpiryDays: null,
|
|
maxNoncurrentVersions: null,
|
|
hasVersioningOverride: false,
|
|
}
|
|
|
|
/** Prototype: derive a bucket's protection config from its name. */
|
|
export const getMockBucketProtection = (bucketName: string | undefined): BucketProtection =>
|
|
(bucketName && PROTECTED_BUCKETS[bucketName]) || DEFAULT_PROTECTION
|
|
|
|
export const isBucketVersioned = (bucketName: string | undefined) =>
|
|
getMockBucketProtection(bucketName).versioning === 'enabled'
|