diff --git a/weed/admin/dash/bucket_management.go b/weed/admin/dash/bucket_management.go index bd488dc90..5942d5695 100644 --- a/weed/admin/dash/bucket_management.go +++ b/weed/admin/dash/bucket_management.go @@ -23,15 +23,16 @@ type S3BucketsData struct { } type CreateBucketRequest struct { - Name string `json:"name" binding:"required"` - Region string `json:"region"` - QuotaSize int64 `json:"quota_size"` // Quota size in bytes - QuotaUnit string `json:"quota_unit"` // Unit: MB, GB, TB - QuotaEnabled bool `json:"quota_enabled"` // Whether quota is enabled - VersioningEnabled bool `json:"versioning_enabled"` // Whether versioning is enabled - ObjectLockEnabled bool `json:"object_lock_enabled"` // Whether object lock is enabled - ObjectLockMode string `json:"object_lock_mode"` // Object lock mode: "GOVERNANCE" or "COMPLIANCE" - ObjectLockDuration int32 `json:"object_lock_duration"` // Default retention duration in days + Name string `json:"name" binding:"required"` + Region string `json:"region"` + QuotaSize int64 `json:"quota_size"` // Quota size in bytes + QuotaUnit string `json:"quota_unit"` // Unit: MB, GB, TB + QuotaEnabled bool `json:"quota_enabled"` // Whether quota is enabled + VersioningEnabled bool `json:"versioning_enabled"` // Whether versioning is enabled + ObjectLockEnabled bool `json:"object_lock_enabled"` // Whether object lock is enabled + ObjectLockMode string `json:"object_lock_mode"` // Object lock mode: "GOVERNANCE" or "COMPLIANCE" + SetDefaultRetention bool `json:"set_default_retention"` // Whether to set default retention + ObjectLockDuration int32 `json:"object_lock_duration"` // Default retention duration in days } // S3 Bucket Management Handlers @@ -105,17 +106,19 @@ func (s *AdminServer) CreateBucket(c *gin.Context) { return } - // Validate retention duration - if req.ObjectLockDuration <= 0 { - c.JSON(http.StatusBadRequest, gin.H{"error": "Object lock duration must be greater than 0 days"}) - return + // Validate retention duration if default retention is enabled + if req.SetDefaultRetention { + if req.ObjectLockDuration <= 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "Object lock duration must be greater than 0 days when default retention is enabled"}) + return + } } } // Convert quota to bytes quotaBytes := convertQuotaToBytes(req.QuotaSize, req.QuotaUnit) - err := s.CreateS3BucketWithObjectLock(req.Name, quotaBytes, req.QuotaEnabled, req.VersioningEnabled, req.ObjectLockEnabled, req.ObjectLockMode, req.ObjectLockDuration) + err := s.CreateS3BucketWithObjectLock(req.Name, quotaBytes, req.QuotaEnabled, req.VersioningEnabled, req.ObjectLockEnabled, req.ObjectLockMode, req.SetDefaultRetention, req.ObjectLockDuration) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create bucket: " + err.Error()}) return @@ -285,11 +288,11 @@ func (s *AdminServer) SetBucketQuota(bucketName string, quotaBytes int64, quotaE // CreateS3BucketWithQuota creates a new S3 bucket with quota settings func (s *AdminServer) CreateS3BucketWithQuota(bucketName string, quotaBytes int64, quotaEnabled bool) error { - return s.CreateS3BucketWithObjectLock(bucketName, quotaBytes, quotaEnabled, false, false, "", 0) + return s.CreateS3BucketWithObjectLock(bucketName, quotaBytes, quotaEnabled, false, false, "", false, 0) } // CreateS3BucketWithObjectLock creates a new S3 bucket with quota, versioning, and object lock settings -func (s *AdminServer) CreateS3BucketWithObjectLock(bucketName string, quotaBytes int64, quotaEnabled, versioningEnabled, objectLockEnabled bool, objectLockMode string, objectLockDuration int32) error { +func (s *AdminServer) CreateS3BucketWithObjectLock(bucketName string, quotaBytes int64, quotaEnabled, versioningEnabled, objectLockEnabled bool, objectLockMode string, setDefaultRetention bool, objectLockDuration int32) error { return s.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error { // First ensure /buckets directory exists _, err := client.CreateEntry(context.Background(), &filer_pb.CreateEntryRequest{ @@ -360,13 +363,17 @@ func (s *AdminServer) CreateS3BucketWithObjectLock(bucketName string, quotaBytes // Handle Object Lock configuration using shared utilities if objectLockEnabled { - // Validate Object Lock parameters - if err := s3api.ValidateObjectLockParameters(objectLockEnabled, objectLockMode, objectLockDuration); err != nil { - return fmt.Errorf("invalid Object Lock parameters: %w", err) + var duration int32 = 0 + if setDefaultRetention { + // Validate Object Lock parameters only when setting default retention + if err := s3api.ValidateObjectLockParameters(objectLockEnabled, objectLockMode, objectLockDuration); err != nil { + return fmt.Errorf("invalid Object Lock parameters: %w", err) + } + duration = objectLockDuration } // Create Object Lock configuration using shared utility - objectLockConfig := s3api.CreateObjectLockConfigurationFromParams(objectLockEnabled, objectLockMode, objectLockDuration) + objectLockConfig := s3api.CreateObjectLockConfigurationFromParams(objectLockEnabled, objectLockMode, duration) // Store Object Lock configuration in extended attributes using shared utility if err := s3api.StoreObjectLockConfigurationInExtended(bucketEntry, objectLockConfig); err != nil { diff --git a/weed/admin/view/app/s3_buckets.templ b/weed/admin/view/app/s3_buckets.templ index 1afafb294..14117ba9f 100644 --- a/weed/admin/view/app/s3_buckets.templ +++ b/weed/admin/view/app/s3_buckets.templ @@ -336,11 +336,22 @@ templ S3Buckets(data dash.S3BucketsData) {
- - -
- Default retention period for new objects (1-36500 days). +
+ + +
+ Apply default retention to all new objects in this bucket. +
+
+
@@ -478,6 +489,8 @@ templ S3Buckets(data dash.S3BucketsData) { const versioningCheckbox = document.getElementById('enableVersioning'); const objectLockCheckbox = document.getElementById('enableObjectLock'); const objectLockSettings = document.getElementById('objectLockSettings'); + const setDefaultRetentionCheckbox = document.getElementById('setDefaultRetention'); + const defaultRetentionSettings = document.getElementById('defaultRetentionSettings'); const createBucketForm = document.getElementById('createBucketForm'); // Toggle quota settings @@ -493,9 +506,17 @@ templ S3Buckets(data dash.S3BucketsData) { versioningCheckbox.disabled = true; } else { versioningCheckbox.disabled = false; + // Reset default retention settings when object lock is disabled + setDefaultRetentionCheckbox.checked = false; + defaultRetentionSettings.style.display = 'none'; } }); + // Toggle default retention settings + setDefaultRetentionCheckbox.addEventListener('change', function() { + defaultRetentionSettings.style.display = this.checked ? 'block' : 'none'; + }); + // Handle form submission createBucketForm.addEventListener('submit', function(e) { e.preventDefault(); @@ -510,11 +531,12 @@ templ S3Buckets(data dash.S3BucketsData) { versioning_enabled: versioningCheckbox.checked, object_lock_enabled: objectLockCheckbox.checked, object_lock_mode: formData.get('object_lock_mode') || 'GOVERNANCE', - object_lock_duration: objectLockCheckbox.checked ? parseInt(formData.get('object_lock_duration')) || 30 : 0 + set_default_retention: setDefaultRetentionCheckbox.checked, + object_lock_duration: setDefaultRetentionCheckbox.checked ? parseInt(formData.get('object_lock_duration')) || 30 : 0 }; // Validate object lock settings - if (data.object_lock_enabled && data.object_lock_duration <= 0) { + if (data.object_lock_enabled && data.set_default_retention && data.object_lock_duration <= 0) { alert('Please enter a valid retention duration for object lock.'); return; } diff --git a/weed/admin/view/app/s3_buckets_templ.go b/weed/admin/view/app/s3_buckets_templ.go index 6edb5d371..ed5703ec2 100644 --- a/weed/admin/view/app/s3_buckets_templ.go +++ b/weed/admin/view/app/s3_buckets_templ.go @@ -379,7 +379,7 @@ func S3Buckets(data dash.S3BucketsData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "
Create New S3 Bucket
Bucket names must be between 3 and 63 characters, contain only lowercase letters, numbers, dots, and hyphens.
Set the maximum storage size for this bucket.
Keep multiple versions of objects in this bucket.
Prevent objects from being deleted or overwritten for a specified period. Automatically enables versioning.
Governance allows override with special permissions, Compliance is immutable.
Default retention period for new objects (1-36500 days).
Delete Bucket

Are you sure you want to delete the bucket ?

Warning: This action cannot be undone. All objects in the bucket will be permanently deleted.
Manage Bucket Quota
Set the maximum storage size for this bucket. Set to 0 to remove quota.
Bucket Details
Loading...
Loading bucket details...
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "
Create New S3 Bucket
Bucket names must be between 3 and 63 characters, contain only lowercase letters, numbers, dots, and hyphens.
Set the maximum storage size for this bucket.
Keep multiple versions of objects in this bucket.
Prevent objects from being deleted or overwritten for a specified period. Automatically enables versioning.
Governance allows override with special permissions, Compliance is immutable.
Apply default retention to all new objects in this bucket.
Default retention period for new objects (1-36500 days).
Delete Bucket

Are you sure you want to delete the bucket ?

Warning: This action cannot be undone. All objects in the bucket will be permanently deleted.
Manage Bucket Quota
Set the maximum storage size for this bucket. Set to 0 to remove quota.
Bucket Details
Loading...
Loading bucket details...
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }