You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							962 lines
						
					
					
						
							47 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							962 lines
						
					
					
						
							47 KiB
						
					
					
				
								package app
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
								    "fmt"
							 | 
						|
								    "github.com/seaweedfs/seaweedfs/weed/admin/dash"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								templ S3Buckets(data dash.S3BucketsData) {
							 | 
						|
								    <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
							 | 
						|
								        <h1 class="h2">
							 | 
						|
								            <i class="fas fa-cube me-2"></i>Object Store Buckets
							 | 
						|
								        </h1>
							 | 
						|
								        <div class="btn-toolbar mb-2 mb-md-0">
							 | 
						|
								            <div class="btn-group me-2">
							 | 
						|
								                <button type="button" class="btn btn-sm btn-primary" 
							 | 
						|
								                        data-bs-toggle="modal" 
							 | 
						|
								                        data-bs-target="#createBucketModal">
							 | 
						|
								                    <i class="fas fa-plus me-1"></i>Create Bucket
							 | 
						|
								                </button>
							 | 
						|
								
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								    </div>
							 | 
						|
								
							 | 
						|
								    <div id="s3-buckets-content">
							 | 
						|
								        <!-- Summary Cards -->
							 | 
						|
								        <div class="row mb-4">
							 | 
						|
								            <div class="col-xl-4 col-md-6 mb-4">
							 | 
						|
								                <div class="card border-left-primary shadow h-100 py-2">
							 | 
						|
								                    <div class="card-body">
							 | 
						|
								                        <div class="row no-gutters align-items-center">
							 | 
						|
								                            <div class="col mr-2">
							 | 
						|
								                                <div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
							 | 
						|
								                                    Total Buckets
							 | 
						|
								                                </div>
							 | 
						|
								                                <div class="h5 mb-0 font-weight-bold text-gray-800">
							 | 
						|
								                                    {fmt.Sprintf("%d", data.TotalBuckets)}
							 | 
						|
								                                </div>
							 | 
						|
								                            </div>
							 | 
						|
								                            <div class="col-auto">
							 | 
						|
								                                <i class="fas fa-cube fa-2x text-gray-300"></i>
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								                    </div>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								
							 | 
						|
								            <div class="col-xl-4 col-md-6 mb-4">
							 | 
						|
								                <div class="card border-left-success shadow h-100 py-2">
							 | 
						|
								                    <div class="card-body">
							 | 
						|
								                        <div class="row no-gutters align-items-center">
							 | 
						|
								                            <div class="col mr-2">
							 | 
						|
								                                <div class="text-xs font-weight-bold text-success text-uppercase mb-1">
							 | 
						|
								                                    Total Storage
							 | 
						|
								                                </div>
							 | 
						|
								                                <div class="h5 mb-0 font-weight-bold text-gray-800">
							 | 
						|
								                                    {formatBytes(data.TotalSize)}
							 | 
						|
								                                </div>
							 | 
						|
								                            </div>
							 | 
						|
								                            <div class="col-auto">
							 | 
						|
								                                <i class="fas fa-hdd fa-2x text-gray-300"></i>
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								                    </div>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								            <div class="col-xl-4 col-md-6 mb-4">
							 | 
						|
								                <div class="card border-left-warning shadow h-100 py-2">
							 | 
						|
								                    <div class="card-body">
							 | 
						|
								                        <div class="row no-gutters align-items-center">
							 | 
						|
								                            <div class="col mr-2">
							 | 
						|
								                                <div class="text-xs font-weight-bold text-warning text-uppercase mb-1">
							 | 
						|
								                                    Last Updated
							 | 
						|
								                                </div>
							 | 
						|
								                                <div class="h6 mb-0 font-weight-bold text-gray-800">
							 | 
						|
								                                    {data.LastUpdated.Format("15:04:05")}
							 | 
						|
								                                </div>
							 | 
						|
								                            </div>
							 | 
						|
								                            <div class="col-auto">
							 | 
						|
								                                <i class="fas fa-clock fa-2x text-gray-300"></i>
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								                    </div>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								
							 | 
						|
								        <!-- Buckets Table -->
							 | 
						|
								        <div class="row">
							 | 
						|
								            <div class="col-12">
							 | 
						|
								                <div class="card shadow mb-4">
							 | 
						|
								                    <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
							 | 
						|
								                        <h6 class="m-0 font-weight-bold text-primary">
							 | 
						|
								                            <i class="fas fa-cube me-2"></i>Object Store Buckets
							 | 
						|
								                        </h6>
							 | 
						|
								                        <div class="dropdown no-arrow">
							 | 
						|
								                            <a class="dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
							 | 
						|
								                                <i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i>
							 | 
						|
								                            </a>
							 | 
						|
								                            <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in">
							 | 
						|
								                                <div class="dropdown-header">Actions:</div>
							 | 
						|
								                                <a class="dropdown-item" href="#" onclick="exportBucketList()">
							 | 
						|
								                                    <i class="fas fa-download me-2"></i>Export List
							 | 
						|
								                                </a>
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								                    </div>
							 | 
						|
								                    <div class="card-body">
							 | 
						|
								                        <div class="table-responsive">
							 | 
						|
								                            <table class="table table-hover" width="100%" cellspacing="0" id="bucketsTable">
							 | 
						|
								                                <thead>
							 | 
						|
								                                    <tr>
							 | 
						|
								                                        <th>Name</th>
							 | 
						|
								                                        <th>Created</th>
							 | 
						|
								                                        <th>Objects</th>
							 | 
						|
								                                        <th>Size</th>
							 | 
						|
								                                        <th>Quota</th>
							 | 
						|
								                                        <th>Versioning</th>
							 | 
						|
								                                        <th>Object Lock</th>
							 | 
						|
								                                        <th>Actions</th>
							 | 
						|
								                                    </tr>
							 | 
						|
								                                </thead>
							 | 
						|
								                                <tbody>
							 | 
						|
								                                    for _, bucket := range data.Buckets {
							 | 
						|
								                                        <tr>
							 | 
						|
								                                            <td>
							 | 
						|
								                                                <a href={templ.SafeURL(fmt.Sprintf("/s3/buckets/%s", bucket.Name))} 
							 | 
						|
								                                                   class="text-decoration-none">
							 | 
						|
								                                                    <i class="fas fa-cube me-2"></i>
							 | 
						|
								                                                    {bucket.Name}
							 | 
						|
								                                                </a>
							 | 
						|
								                                            </td>
							 | 
						|
								                                            <td>{bucket.CreatedAt.Format("2006-01-02 15:04")}</td>
							 | 
						|
								                                            <td>{fmt.Sprintf("%d", bucket.ObjectCount)}</td>
							 | 
						|
								                                            <td>{formatBytes(bucket.Size)}</td>
							 | 
						|
								                                            <td>
							 | 
						|
								                                                if bucket.Quota > 0 {
							 | 
						|
								                                                    <div>
							 | 
						|
								                                                        <span class={fmt.Sprintf("badge bg-%s", getQuotaStatusColor(bucket.Size, bucket.Quota, bucket.QuotaEnabled))}>
							 | 
						|
								                                                            {formatBytes(bucket.Quota)}
							 | 
						|
								                                                        </span>
							 | 
						|
								                                                        if bucket.QuotaEnabled {
							 | 
						|
								                                                            <div class="small text-muted">
							 | 
						|
								                                                                {fmt.Sprintf("%.1f%% used", float64(bucket.Size)/float64(bucket.Quota)*100)}
							 | 
						|
								                                                            </div>
							 | 
						|
								                                                        } else {
							 | 
						|
								                                                            <div class="small text-muted">Disabled</div>
							 | 
						|
								                                                        }
							 | 
						|
								                                                    </div>
							 | 
						|
								                                                } else {
							 | 
						|
								                                                    <span class="text-muted">No quota</span>
							 | 
						|
								                                                }
							 | 
						|
								                                            </td>
							 | 
						|
								                                            <td>
							 | 
						|
								                                                if bucket.VersioningEnabled {
							 | 
						|
								                                                    <span class="badge bg-success">
							 | 
						|
								                                                        <i class="fas fa-check me-1"></i>Enabled
							 | 
						|
								                                                    </span>
							 | 
						|
								                                                } else {
							 | 
						|
								                                                    <span class="badge bg-secondary">
							 | 
						|
								                                                        <i class="fas fa-times me-1"></i>Disabled
							 | 
						|
								                                                    </span>
							 | 
						|
								                                                }
							 | 
						|
								                                            </td>
							 | 
						|
								                                            <td>
							 | 
						|
								                                                if bucket.ObjectLockEnabled {
							 | 
						|
								                                                    <div>
							 | 
						|
								                                                        <span class="badge bg-warning">
							 | 
						|
								                                                            <i class="fas fa-lock me-1"></i>Enabled
							 | 
						|
								                                                        </span>
							 | 
						|
								                                                        <div class="small text-muted">
							 | 
						|
								                                                            {bucket.ObjectLockMode} • {fmt.Sprintf("%d days", bucket.ObjectLockDuration)}
							 | 
						|
								                                                        </div>
							 | 
						|
								                                                    </div>
							 | 
						|
								                                                } else {
							 | 
						|
								                                                    <span class="badge bg-secondary">
							 | 
						|
								                                                        <i class="fas fa-unlock me-1"></i>Disabled
							 | 
						|
								                                                    </span>
							 | 
						|
								                                                }
							 | 
						|
								                                            </td>
							 | 
						|
								                                            <td>
							 | 
						|
								                                                <div class="btn-group btn-group-sm" role="group">
							 | 
						|
								                                                    <a href={templ.SafeURL(fmt.Sprintf("/files?path=/buckets/%s", bucket.Name))} 
							 | 
						|
								                                                       class="btn btn-outline-success btn-sm"
							 | 
						|
								                                                       title="Browse Files">
							 | 
						|
								                                                        <i class="fas fa-folder-open"></i>
							 | 
						|
								                                                    </a>
							 | 
						|
								                                                    <button type="button" 
							 | 
						|
								                                                            class="btn btn-outline-primary btn-sm view-details-btn"
							 | 
						|
								                                                            data-bucket-name={bucket.Name}
							 | 
						|
								                                                            title="View Details">
							 | 
						|
								                                                        <i class="fas fa-eye"></i>
							 | 
						|
								                                                    </button>
							 | 
						|
								                                                    <button type="button" 
							 | 
						|
								                                                            class="btn btn-outline-warning btn-sm quota-btn"
							 | 
						|
								                                                            data-bucket-name={bucket.Name}
							 | 
						|
								                                                            data-current-quota={fmt.Sprintf("%d", getQuotaInMB(bucket.Quota))}
							 | 
						|
								                                                            data-quota-enabled={fmt.Sprintf("%t", bucket.QuotaEnabled)}
							 | 
						|
								                                                            title="Manage Quota">
							 | 
						|
								                                                        <i class="fas fa-tachometer-alt"></i>
							 | 
						|
								                                                    </button>
							 | 
						|
								                                                    <button type="button" 
							 | 
						|
								                                                            class="btn btn-outline-danger btn-sm delete-bucket-btn"
							 | 
						|
								                                                            data-bucket-name={bucket.Name}
							 | 
						|
								                                                            title="Delete Bucket">
							 | 
						|
								                                                        <i class="fas fa-trash"></i>
							 | 
						|
								                                                    </button>
							 | 
						|
								                                                </div>
							 | 
						|
								                                            </td>
							 | 
						|
								                                        </tr>
							 | 
						|
								                                    }
							 | 
						|
								                                    if len(data.Buckets) == 0 {
							 | 
						|
								                                        <tr>
							 | 
						|
								                                            <td colspan="8" class="text-center text-muted py-4">
							 | 
						|
								                                                <i class="fas fa-cube fa-3x mb-3 text-muted"></i>
							 | 
						|
								                                                <div>
							 | 
						|
								                                                    					<h5>No Object Store buckets found</h5>
							 | 
						|
								                                                    <p>Create your first bucket to get started with S3 storage.</p>
							 | 
						|
								                                                    <button type="button" class="btn btn-primary" 
							 | 
						|
								                                                            data-bs-toggle="modal" 
							 | 
						|
								                                                            data-bs-target="#createBucketModal">
							 | 
						|
								                                                        <i class="fas fa-plus me-1"></i>Create Bucket
							 | 
						|
								                                                    </button>
							 | 
						|
								                                                </div>
							 | 
						|
								                                            </td>
							 | 
						|
								                                        </tr>
							 | 
						|
								                                    }
							 | 
						|
								                                </tbody>
							 | 
						|
								                            </table>
							 | 
						|
								                        </div>
							 | 
						|
								                    </div>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								
							 | 
						|
								        <!-- Last Updated -->
							 | 
						|
								        <div class="row">
							 | 
						|
								            <div class="col-12">
							 | 
						|
								                <small class="text-muted">
							 | 
						|
								                    <i class="fas fa-clock me-1"></i>
							 | 
						|
								                    Last updated: {data.LastUpdated.Format("2006-01-02 15:04:05")}
							 | 
						|
								                </small>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								    </div>
							 | 
						|
								
							 | 
						|
								    <!-- Create Bucket Modal -->
							 | 
						|
								    <div class="modal fade" id="createBucketModal" tabindex="-1" aria-labelledby="createBucketModalLabel" aria-hidden="true">
							 | 
						|
								        <div class="modal-dialog">
							 | 
						|
								            <div class="modal-content">
							 | 
						|
								                <div class="modal-header">
							 | 
						|
								                    <h5 class="modal-title" id="createBucketModalLabel">
							 | 
						|
								                        <i class="fas fa-plus me-2"></i>Create New S3 Bucket
							 | 
						|
								                    </h5>
							 | 
						|
								                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
							 | 
						|
								                </div>
							 | 
						|
								                <form id="createBucketForm">
							 | 
						|
								                    <div class="modal-body">
							 | 
						|
								                        <div class="mb-3">
							 | 
						|
								                            <label for="bucketName" class="form-label">Bucket Name</label>
							 | 
						|
								                            <input type="text" class="form-control" id="bucketName" name="name" 
							 | 
						|
								                                   placeholder="my-bucket-name" required
							 | 
						|
								                                   pattern="[a-z0-9.-]+" 
							 | 
						|
								                                   title="Bucket name must contain only lowercase letters, numbers, dots, and hyphens">
							 | 
						|
								                            <div class="form-text">
							 | 
						|
								                                Bucket names must be between 3 and 63 characters, contain only lowercase letters, numbers, dots, and hyphens.
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								                        
							 | 
						|
								                        <div class="mb-3">
							 | 
						|
								                            <div class="form-check">
							 | 
						|
								                                <input class="form-check-input" type="checkbox" id="enableQuota" name="quota_enabled">
							 | 
						|
								                                <label class="form-check-label" for="enableQuota">
							 | 
						|
								                                    Enable Storage Quota
							 | 
						|
								                                </label>
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								                        
							 | 
						|
								                        <div class="mb-3" id="quotaSettings" style="display: none;">
							 | 
						|
								                            <div class="row">
							 | 
						|
								                                <div class="col-md-8">
							 | 
						|
								                                    <label for="quotaSize" class="form-label">Quota Size</label>
							 | 
						|
								                                    <input type="number" class="form-control" id="quotaSize" name="quota_size" 
							 | 
						|
								                                           placeholder="1024" min="1" step="1">
							 | 
						|
								                                </div>
							 | 
						|
								                                <div class="col-md-4">
							 | 
						|
								                                    <label for="quotaUnit" class="form-label">Unit</label>
							 | 
						|
								                                    <select class="form-select" id="quotaUnit" name="quota_unit">
							 | 
						|
								                                        <option value="MB" selected>MB</option>
							 | 
						|
								                                        <option value="GB">GB</option>
							 | 
						|
								                                        <option value="TB">TB</option>
							 | 
						|
								                                    </select>
							 | 
						|
								                                </div>
							 | 
						|
								                            </div>
							 | 
						|
								                            <div class="form-text">
							 | 
						|
								                                Set the maximum storage size for this bucket.
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								
							 | 
						|
								                        <div class="mb-3">
							 | 
						|
								                            <div class="form-check">
							 | 
						|
								                                <input class="form-check-input" type="checkbox" id="enableVersioning" name="versioning_enabled">
							 | 
						|
								                                <label class="form-check-label" for="enableVersioning">
							 | 
						|
								                                    Enable Object Versioning
							 | 
						|
								                                </label>
							 | 
						|
								                            </div>
							 | 
						|
								                            <div class="form-text">
							 | 
						|
								                                Keep multiple versions of objects in this bucket.
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								
							 | 
						|
								                        <div class="mb-3">
							 | 
						|
								                            <div class="form-check">
							 | 
						|
								                                <input class="form-check-input" type="checkbox" id="enableObjectLock" name="object_lock_enabled">
							 | 
						|
								                                <label class="form-check-label" for="enableObjectLock">
							 | 
						|
								                                    Enable Object Lock
							 | 
						|
								                                </label>
							 | 
						|
								                            </div>
							 | 
						|
								                            <div class="form-text">
							 | 
						|
								                                Prevent objects from being deleted or overwritten for a specified period. Automatically enables versioning.
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								
							 | 
						|
								                        <div class="mb-3" id="objectLockSettings" style="display: none;">
							 | 
						|
								                            <div class="row">
							 | 
						|
								                                <div class="col-md-6">
							 | 
						|
								                                    <label for="objectLockMode" class="form-label">Object Lock Mode</label>
							 | 
						|
								                                    <select class="form-select" id="objectLockMode" name="object_lock_mode">
							 | 
						|
								                                        <option value="GOVERNANCE" selected>Governance</option>
							 | 
						|
								                                        <option value="COMPLIANCE">Compliance</option>
							 | 
						|
								                                    </select>
							 | 
						|
								                                    <div class="form-text">
							 | 
						|
								                                        Governance allows override with special permissions, Compliance is immutable.
							 | 
						|
								                                    </div>
							 | 
						|
								                                </div>
							 | 
						|
								                                <div class="col-md-6">
							 | 
						|
								                                    <div class="form-check mb-3">
							 | 
						|
								                                        <input class="form-check-input" type="checkbox" id="setDefaultRetention" name="set_default_retention">
							 | 
						|
								                                        <label class="form-check-label" for="setDefaultRetention">
							 | 
						|
								                                            Set Default Retention
							 | 
						|
								                                        </label>
							 | 
						|
								                                        <div class="form-text">
							 | 
						|
								                                            Apply default retention to all new objects in this bucket.
							 | 
						|
								                                        </div>
							 | 
						|
								                                    </div>
							 | 
						|
								                                    <div id="defaultRetentionSettings" style="display: none;">
							 | 
						|
								                                        <label for="objectLockDuration" class="form-label">Default Retention (days)</label>
							 | 
						|
								                                        <input type="number" class="form-control" id="objectLockDuration" name="object_lock_duration" 
							 | 
						|
								                                               placeholder="30" min="1" max="36500" step="1">
							 | 
						|
								                                        <div class="form-text">
							 | 
						|
								                                            Default retention period for new objects (1-36500 days).
							 | 
						|
								                                        </div>
							 | 
						|
								                                    </div>
							 | 
						|
								                                </div>
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								
							 | 
						|
								                    </div>
							 | 
						|
								                    <div class="modal-footer">
							 | 
						|
								                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
							 | 
						|
								                        <button type="submit" class="btn btn-primary">
							 | 
						|
								                            <i class="fas fa-plus me-1"></i>Create Bucket
							 | 
						|
								                        </button>
							 | 
						|
								                    </div>
							 | 
						|
								                </form>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								    </div>
							 | 
						|
								
							 | 
						|
								    <!-- Delete Confirmation Modal -->
							 | 
						|
								    <div class="modal fade" id="deleteBucketModal" tabindex="-1" aria-labelledby="deleteBucketModalLabel" aria-hidden="true">
							 | 
						|
								        <div class="modal-dialog">
							 | 
						|
								            <div class="modal-content">
							 | 
						|
								                <div class="modal-header">
							 | 
						|
								                    <h5 class="modal-title" id="deleteBucketModalLabel">
							 | 
						|
								                        <i class="fas fa-exclamation-triangle me-2 text-warning"></i>Delete Bucket
							 | 
						|
								                    </h5>
							 | 
						|
								                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-body">
							 | 
						|
								                    <p>Are you sure you want to delete the bucket <strong id="deleteBucketName"></strong>?</p>
							 | 
						|
								                    <div class="alert alert-warning">
							 | 
						|
								                        <i class="fas fa-exclamation-triangle me-2"></i>
							 | 
						|
								                        <strong>Warning:</strong> This action cannot be undone. All objects in the bucket will be permanently deleted.
							 | 
						|
								                    </div>
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-footer">
							 | 
						|
								                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
							 | 
						|
								                    <button type="button" class="btn btn-danger" onclick="deleteBucket()">
							 | 
						|
								                        <i class="fas fa-trash me-1"></i>Delete Bucket
							 | 
						|
								                    </button>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								    </div>
							 | 
						|
								
							 | 
						|
								    <!-- Manage Quota Modal -->
							 | 
						|
								    <div class="modal fade" id="manageQuotaModal" tabindex="-1" aria-labelledby="manageQuotaModalLabel" aria-hidden="true">
							 | 
						|
								        <div class="modal-dialog">
							 | 
						|
								            <div class="modal-content">
							 | 
						|
								                <div class="modal-header">
							 | 
						|
								                    <h5 class="modal-title" id="manageQuotaModalLabel">
							 | 
						|
								                        <i class="fas fa-tachometer-alt me-2"></i>Manage Bucket Quota
							 | 
						|
								                    </h5>
							 | 
						|
								                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
							 | 
						|
								                </div>
							 | 
						|
								                <form id="quotaForm">
							 | 
						|
								                    <div class="modal-body">
							 | 
						|
								                        <div class="mb-3">
							 | 
						|
								                            <label class="form-label">Bucket Name</label>
							 | 
						|
								                            <input type="text" class="form-control" id="quotaBucketName" readonly>
							 | 
						|
								                        </div>
							 | 
						|
								                        
							 | 
						|
								                        <div class="mb-3">
							 | 
						|
								                            <div class="form-check">
							 | 
						|
								                                <input class="form-check-input" type="checkbox" id="quotaEnabled" name="quota_enabled">
							 | 
						|
								                                <label class="form-check-label" for="quotaEnabled">
							 | 
						|
								                                    Enable Storage Quota
							 | 
						|
								                                </label>
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								                        
							 | 
						|
								                        <div class="mb-3" id="quotaSizeSettings">
							 | 
						|
								                            <div class="row">
							 | 
						|
								                                <div class="col-md-8">
							 | 
						|
								                                    <label for="quotaSizeMB" class="form-label">Quota Size</label>
							 | 
						|
								                                    <input type="number" class="form-control" id="quotaSizeMB" name="quota_size" 
							 | 
						|
								                                           placeholder="1024" min="0" step="1">
							 | 
						|
								                                </div>
							 | 
						|
								                                <div class="col-md-4">
							 | 
						|
								                                    <label for="quotaUnitMB" class="form-label">Unit</label>
							 | 
						|
								                                    <select class="form-select" id="quotaUnitMB" name="quota_unit">
							 | 
						|
								                                        <option value="MB" selected>MB</option>
							 | 
						|
								                                        <option value="GB">GB</option>
							 | 
						|
								                                        <option value="TB">TB</option>
							 | 
						|
								                                    </select>
							 | 
						|
								                                </div>
							 | 
						|
								                            </div>
							 | 
						|
								                            <div class="form-text">
							 | 
						|
								                                Set the maximum storage size for this bucket. Set to 0 to remove quota.
							 | 
						|
								                            </div>
							 | 
						|
								                        </div>
							 | 
						|
								                    </div>
							 | 
						|
								                    <div class="modal-footer">
							 | 
						|
								                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
							 | 
						|
								                        <button type="submit" class="btn btn-warning">
							 | 
						|
								                            <i class="fas fa-save me-1"></i>Update Quota
							 | 
						|
								                        </button>
							 | 
						|
								                    </div>
							 | 
						|
								                </form>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								    </div>
							 | 
						|
								
							 | 
						|
								    <!-- Bucket Details Modal -->
							 | 
						|
								    <div class="modal fade" id="bucketDetailsModal" tabindex="-1" aria-labelledby="bucketDetailsModalLabel" aria-hidden="true">
							 | 
						|
								        <div class="modal-dialog modal-lg">
							 | 
						|
								            <div class="modal-content">
							 | 
						|
								                <div class="modal-header">
							 | 
						|
								                    <h5 class="modal-title" id="bucketDetailsModalLabel">
							 | 
						|
								                        <i class="fas fa-cube me-2"></i>Bucket Details
							 | 
						|
								                    </h5>
							 | 
						|
								                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-body">
							 | 
						|
								                    <div id="bucketDetailsContent">
							 | 
						|
								                        <div class="text-center py-4">
							 | 
						|
								                            <div class="spinner-border text-primary" role="status">
							 | 
						|
								                                <span class="visually-hidden">Loading...</span>
							 | 
						|
								                            </div>
							 | 
						|
								                            <div class="mt-2">Loading bucket details...</div>
							 | 
						|
								                        </div>
							 | 
						|
								                    </div>
							 | 
						|
								                </div>
							 | 
						|
								                <div class="modal-footer">
							 | 
						|
								                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
							 | 
						|
								                </div>
							 | 
						|
								            </div>
							 | 
						|
								        </div>
							 | 
						|
								    </div>
							 | 
						|
								
							 | 
						|
								    <!-- JavaScript for bucket management -->
							 | 
						|
								    <script>
							 | 
						|
								    document.addEventListener('DOMContentLoaded', function() {
							 | 
						|
								        const quotaCheckbox = document.getElementById('enableQuota');
							 | 
						|
								        const quotaSettings = document.getElementById('quotaSettings');
							 | 
						|
								        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
							 | 
						|
								        quotaCheckbox.addEventListener('change', function() {
							 | 
						|
								            quotaSettings.style.display = this.checked ? 'block' : 'none';
							 | 
						|
								        });
							 | 
						|
								
							 | 
						|
								        // Toggle object lock settings and automatically enable versioning
							 | 
						|
								        objectLockCheckbox.addEventListener('change', function() {
							 | 
						|
								            objectLockSettings.style.display = this.checked ? 'block' : 'none';
							 | 
						|
								            if (this.checked) {
							 | 
						|
								                versioningCheckbox.checked = true;
							 | 
						|
								                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();
							 | 
						|
								            
							 | 
						|
								            const formData = new FormData(this);
							 | 
						|
								            const data = {
							 | 
						|
								                name: formData.get('name'),
							 | 
						|
								                region: formData.get('region') || '',
							 | 
						|
								                quota_size: quotaCheckbox.checked ? parseInt(formData.get('quota_size')) || 0 : 0,
							 | 
						|
								                quota_unit: formData.get('quota_unit') || 'MB',
							 | 
						|
								                quota_enabled: quotaCheckbox.checked,
							 | 
						|
								                versioning_enabled: versioningCheckbox.checked,
							 | 
						|
								                object_lock_enabled: objectLockCheckbox.checked,
							 | 
						|
								                object_lock_mode: formData.get('object_lock_mode') || 'GOVERNANCE',
							 | 
						|
								                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.set_default_retention && data.object_lock_duration <= 0) {
							 | 
						|
								                alert('Please enter a valid retention duration for object lock.');
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            fetch('/api/s3/buckets', {
							 | 
						|
								                method: 'POST',
							 | 
						|
								                headers: {
							 | 
						|
								                    'Content-Type': 'application/json',
							 | 
						|
								                },
							 | 
						|
								                body: JSON.stringify(data)
							 | 
						|
								            })
							 | 
						|
								            .then(response => response.json())
							 | 
						|
								            .then(data => {
							 | 
						|
								                if (data.error) {
							 | 
						|
								                    alert('Error creating bucket: ' + data.error);
							 | 
						|
								                } else {
							 | 
						|
								                    alert('Bucket created successfully!');
							 | 
						|
								                    // Properly close the modal before reloading
							 | 
						|
								                    const createModal = bootstrap.Modal.getInstance(document.getElementById('createBucketModal'));
							 | 
						|
								                    if (createModal) {
							 | 
						|
								                        createModal.hide();
							 | 
						|
								                    }
							 | 
						|
								                    setTimeout(() => location.reload(), 500);
							 | 
						|
								                }
							 | 
						|
								            })
							 | 
						|
								            .catch(error => {
							 | 
						|
								                console.error('Error:', error);
							 | 
						|
								                alert('Error creating bucket: ' + error.message);
							 | 
						|
								            });
							 | 
						|
								        });
							 | 
						|
								
							 | 
						|
								        // Handle delete bucket
							 | 
						|
								        let deleteModalInstance = null;
							 | 
						|
								        document.querySelectorAll('.delete-bucket-btn').forEach(button => {
							 | 
						|
								            button.addEventListener('click', function() {
							 | 
						|
								                const bucketName = this.dataset.bucketName;
							 | 
						|
								                document.getElementById('deleteBucketName').textContent = bucketName;
							 | 
						|
								                window.currentBucketToDelete = bucketName;
							 | 
						|
								                
							 | 
						|
								                // Dispose of existing modal instance if it exists
							 | 
						|
								                if (deleteModalInstance) {
							 | 
						|
								                    deleteModalInstance.dispose();
							 | 
						|
								                }
							 | 
						|
								                
							 | 
						|
								                // Create new modal instance
							 | 
						|
								                deleteModalInstance = new bootstrap.Modal(document.getElementById('deleteBucketModal'));
							 | 
						|
								                deleteModalInstance.show();
							 | 
						|
								            });
							 | 
						|
								        });
							 | 
						|
								
							 | 
						|
								        // Add event listener to properly dispose of delete modal when hidden
							 | 
						|
								        document.getElementById('deleteBucketModal').addEventListener('hidden.bs.modal', function() {
							 | 
						|
								            if (deleteModalInstance) {
							 | 
						|
								                deleteModalInstance.dispose();
							 | 
						|
								                deleteModalInstance = null;
							 | 
						|
								            }
							 | 
						|
								            // Force remove any remaining backdrops
							 | 
						|
								            document.querySelectorAll('.modal-backdrop').forEach(backdrop => {
							 | 
						|
								                backdrop.remove();
							 | 
						|
								            });
							 | 
						|
								            // Ensure body classes are removed
							 | 
						|
								            document.body.classList.remove('modal-open');
							 | 
						|
								            document.body.style.removeProperty('padding-right');
							 | 
						|
								        });
							 | 
						|
								
							 | 
						|
								        // Handle quota management
							 | 
						|
								        let quotaModalInstance = null;
							 | 
						|
								        document.querySelectorAll('.quota-btn').forEach(button => {
							 | 
						|
								            button.addEventListener('click', function() {
							 | 
						|
								                const bucketName = this.dataset.bucketName;
							 | 
						|
								                const currentQuota = parseInt(this.dataset.currentQuota);
							 | 
						|
								                const quotaEnabled = this.dataset.quotaEnabled === 'true';
							 | 
						|
								                
							 | 
						|
								                document.getElementById('quotaBucketName').value = bucketName;
							 | 
						|
								                document.getElementById('quotaEnabled').checked = quotaEnabled;
							 | 
						|
								                document.getElementById('quotaSizeMB').value = currentQuota;
							 | 
						|
								                
							 | 
						|
								                // Toggle quota size settings
							 | 
						|
								                document.getElementById('quotaSizeSettings').style.display = quotaEnabled ? 'block' : 'none';
							 | 
						|
								                
							 | 
						|
								                window.currentBucketToUpdate = bucketName;
							 | 
						|
								                
							 | 
						|
								                // Dispose of existing modal instance if it exists
							 | 
						|
								                if (quotaModalInstance) {
							 | 
						|
								                    quotaModalInstance.dispose();
							 | 
						|
								                }
							 | 
						|
								                
							 | 
						|
								                // Create new modal instance
							 | 
						|
								                quotaModalInstance = new bootstrap.Modal(document.getElementById('manageQuotaModal'));
							 | 
						|
								                quotaModalInstance.show();
							 | 
						|
								            });
							 | 
						|
								        });
							 | 
						|
								
							 | 
						|
								        // Add event listener to properly dispose of quota modal when hidden
							 | 
						|
								        document.getElementById('manageQuotaModal').addEventListener('hidden.bs.modal', function() {
							 | 
						|
								            if (quotaModalInstance) {
							 | 
						|
								                quotaModalInstance.dispose();
							 | 
						|
								                quotaModalInstance = null;
							 | 
						|
								            }
							 | 
						|
								            // Force remove any remaining backdrops
							 | 
						|
								            document.querySelectorAll('.modal-backdrop').forEach(backdrop => {
							 | 
						|
								                backdrop.remove();
							 | 
						|
								            });
							 | 
						|
								            // Ensure body classes are removed
							 | 
						|
								            document.body.classList.remove('modal-open');
							 | 
						|
								            document.body.style.removeProperty('padding-right');
							 | 
						|
								        });
							 | 
						|
								
							 | 
						|
								        // Handle quota form submission
							 | 
						|
								        document.getElementById('quotaForm').addEventListener('submit', function(e) {
							 | 
						|
								            e.preventDefault();
							 | 
						|
								            
							 | 
						|
								            const formData = new FormData(this);
							 | 
						|
								            const enabled = document.getElementById('quotaEnabled').checked;
							 | 
						|
								            const data = {
							 | 
						|
								                quota_size: enabled ? parseInt(formData.get('quota_size')) || 0 : 0,
							 | 
						|
								                quota_unit: formData.get('quota_unit') || 'MB',
							 | 
						|
								                quota_enabled: enabled
							 | 
						|
								            };
							 | 
						|
								
							 | 
						|
								            fetch(`/api/s3/buckets/${window.currentBucketToUpdate}/quota`, {
							 | 
						|
								                method: 'PUT',
							 | 
						|
								                headers: {
							 | 
						|
								                    'Content-Type': 'application/json',
							 | 
						|
								                },
							 | 
						|
								                body: JSON.stringify(data)
							 | 
						|
								            })
							 | 
						|
								            .then(response => response.json())
							 | 
						|
								            .then(data => {
							 | 
						|
								                if (data.error) {
							 | 
						|
								                    alert('Error updating quota: ' + data.error);
							 | 
						|
								                } else {
							 | 
						|
								                    alert('Quota updated successfully!');
							 | 
						|
								                    // Properly close the modal before reloading
							 | 
						|
								                    if (quotaModalInstance) {
							 | 
						|
								                        quotaModalInstance.hide();
							 | 
						|
								                    }
							 | 
						|
								                    setTimeout(() => location.reload(), 500);
							 | 
						|
								                }
							 | 
						|
								            })
							 | 
						|
								            .catch(error => {
							 | 
						|
								                console.error('Error:', error);
							 | 
						|
								                alert('Error updating quota: ' + error.message);
							 | 
						|
								            });
							 | 
						|
								        });
							 | 
						|
								
							 | 
						|
								        // Handle quota enabled checkbox
							 | 
						|
								        document.getElementById('quotaEnabled').addEventListener('change', function() {
							 | 
						|
								            document.getElementById('quotaSizeSettings').style.display = this.checked ? 'block' : 'none';
							 | 
						|
								        });
							 | 
						|
								
							 | 
						|
								        // Handle view details button
							 | 
						|
								        let detailsModalInstance = null;
							 | 
						|
								        document.querySelectorAll('.view-details-btn').forEach(button => {
							 | 
						|
								            button.addEventListener('click', function() {
							 | 
						|
								                const bucketName = this.dataset.bucketName;
							 | 
						|
								                
							 | 
						|
								                // Update modal title
							 | 
						|
								                document.getElementById('bucketDetailsModalLabel').innerHTML = 
							 | 
						|
								                    '<i class="fas fa-cube me-2"></i>Bucket Details - ' + bucketName;
							 | 
						|
								                
							 | 
						|
								                // Show loading spinner
							 | 
						|
								                document.getElementById('bucketDetailsContent').innerHTML = 
							 | 
						|
								                    '<div class="text-center py-4">' +
							 | 
						|
								                    '<div class="spinner-border text-primary" role="status">' +
							 | 
						|
								                    '<span class="visually-hidden">Loading...</span>' +
							 | 
						|
								                    '</div>' +
							 | 
						|
								                    '<div class="mt-2">Loading bucket details...</div>' +
							 | 
						|
								                    '</div>';
							 | 
						|
								                
							 | 
						|
								                // Dispose of existing modal instance if it exists
							 | 
						|
								                if (detailsModalInstance) {
							 | 
						|
								                    detailsModalInstance.dispose();
							 | 
						|
								                }
							 | 
						|
								                
							 | 
						|
								                // Create new modal instance
							 | 
						|
								                detailsModalInstance = new bootstrap.Modal(document.getElementById('bucketDetailsModal'));
							 | 
						|
								                detailsModalInstance.show();
							 | 
						|
								                
							 | 
						|
								                // Fetch bucket details
							 | 
						|
								                fetch('/api/s3/buckets/' + bucketName)
							 | 
						|
								                    .then(response => response.json())
							 | 
						|
								                    .then(data => {
							 | 
						|
								                        if (data.error) {
							 | 
						|
								                            document.getElementById('bucketDetailsContent').innerHTML = 
							 | 
						|
								                                '<div class="alert alert-danger">' +
							 | 
						|
								                                '<i class="fas fa-exclamation-triangle me-2"></i>' +
							 | 
						|
								                                'Error loading bucket details: ' + data.error +
							 | 
						|
								                                '</div>';
							 | 
						|
								                        } else {
							 | 
						|
								                            displayBucketDetails(data);
							 | 
						|
								                        }
							 | 
						|
								                    })
							 | 
						|
								                    .catch(error => {
							 | 
						|
								                        console.error('Error fetching bucket details:', error);
							 | 
						|
								                        document.getElementById('bucketDetailsContent').innerHTML = 
							 | 
						|
								                            '<div class="alert alert-danger">' +
							 | 
						|
								                            '<i class="fas fa-exclamation-triangle me-2"></i>' +
							 | 
						|
								                            'Error loading bucket details: ' + error.message +
							 | 
						|
								                            '</div>';
							 | 
						|
								                    });
							 | 
						|
								            });
							 | 
						|
								        });
							 | 
						|
								
							 | 
						|
								        // Add event listener to properly dispose of details modal when hidden
							 | 
						|
								        document.getElementById('bucketDetailsModal').addEventListener('hidden.bs.modal', function() {
							 | 
						|
								            if (detailsModalInstance) {
							 | 
						|
								                detailsModalInstance.dispose();
							 | 
						|
								                detailsModalInstance = null;
							 | 
						|
								            }
							 | 
						|
								            // Force remove any remaining backdrops
							 | 
						|
								            document.querySelectorAll('.modal-backdrop').forEach(backdrop => {
							 | 
						|
								                backdrop.remove();
							 | 
						|
								            });
							 | 
						|
								            // Ensure body classes are removed
							 | 
						|
								            document.body.classList.remove('modal-open');
							 | 
						|
								            document.body.style.removeProperty('padding-right');
							 | 
						|
								        });
							 | 
						|
								    });
							 | 
						|
								
							 | 
						|
								    function deleteBucket() {
							 | 
						|
								        const bucketName = window.currentBucketToDelete;
							 | 
						|
								        if (!bucketName) return;
							 | 
						|
								
							 | 
						|
								        fetch(`/api/s3/buckets/${bucketName}`, {
							 | 
						|
								            method: 'DELETE'
							 | 
						|
								        })
							 | 
						|
								        .then(response => response.json())
							 | 
						|
								        .then(data => {
							 | 
						|
								            if (data.error) {
							 | 
						|
								                alert('Error deleting bucket: ' + data.error);
							 | 
						|
								            } else {
							 | 
						|
								                alert('Bucket deleted successfully!');
							 | 
						|
								                // Properly close the modal before reloading
							 | 
						|
								                if (deleteModalInstance) {
							 | 
						|
								                    deleteModalInstance.hide();
							 | 
						|
								                }
							 | 
						|
								                setTimeout(() => location.reload(), 500);
							 | 
						|
								            }
							 | 
						|
								        })
							 | 
						|
								        .catch(error => {
							 | 
						|
								            console.error('Error:', error);
							 | 
						|
								            alert('Error deleting bucket: ' + error.message);
							 | 
						|
								        });
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    function displayBucketDetails(data) {
							 | 
						|
								        const bucket = data.bucket;
							 | 
						|
								        const objects = data.objects || [];
							 | 
						|
								        
							 | 
						|
								        // Helper function to format bytes
							 | 
						|
								        function formatBytes(bytes) {
							 | 
						|
								            if (bytes === 0) return '0 Bytes';
							 | 
						|
								            const k = 1024;
							 | 
						|
								            const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
							 | 
						|
								            const i = Math.floor(Math.log(bytes) / Math.log(k));
							 | 
						|
								            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
							 | 
						|
								        }
							 | 
						|
								        
							 | 
						|
								        // Helper function to format date
							 | 
						|
								        function formatDate(dateString) {
							 | 
						|
								            const date = new Date(dateString);
							 | 
						|
								            return date.toLocaleString();
							 | 
						|
								        }
							 | 
						|
								        
							 | 
						|
								        // Generate objects table
							 | 
						|
								        let objectsTable = '';
							 | 
						|
								        if (objects.length > 0) {
							 | 
						|
								            objectsTable = '<div class="table-responsive">' +
							 | 
						|
								                '<table class="table table-sm table-striped">' +
							 | 
						|
								                '<thead>' +
							 | 
						|
								                '<tr>' +
							 | 
						|
								                '<th>Object Key</th>' +
							 | 
						|
								                '<th>Size</th>' +
							 | 
						|
								                '<th>Last Modified</th>' +
							 | 
						|
								                '<th>Storage Class</th>' +
							 | 
						|
								                '</tr>' +
							 | 
						|
								                '</thead>' +
							 | 
						|
								                '<tbody>' +
							 | 
						|
								                objects.map(obj => 
							 | 
						|
								                    '<tr>' +
							 | 
						|
								                    '<td><i class="fas fa-file me-1"></i>' + obj.key + '</td>' +
							 | 
						|
								                    '<td>' + formatBytes(obj.size) + '</td>' +
							 | 
						|
								                    '<td>' + formatDate(obj.last_modified) + '</td>' +
							 | 
						|
								                    '<td><span class="badge bg-primary">' + obj.storage_class + '</span></td>' +
							 | 
						|
								                    '</tr>'
							 | 
						|
								                ).join('') +
							 | 
						|
								                '</tbody>' +
							 | 
						|
								                '</table>' +
							 | 
						|
								                '</div>';
							 | 
						|
								        } else {
							 | 
						|
								            objectsTable = '<div class="text-center py-4 text-muted">' +
							 | 
						|
								                '<i class="fas fa-file fa-3x mb-3"></i>' +
							 | 
						|
								                '<div>No objects found in this bucket</div>' +
							 | 
						|
								                '</div>';
							 | 
						|
								        }
							 | 
						|
								        
							 | 
						|
								        const content = '<div class="row">' +
							 | 
						|
								            '<div class="col-md-6">' +
							 | 
						|
								            '<h6><i class="fas fa-info-circle me-2"></i>Bucket Information</h6>' +
							 | 
						|
								            '<table class="table table-sm">' +
							 | 
						|
								            '<tr>' +
							 | 
						|
								            '<td><strong>Name:</strong></td>' +
							 | 
						|
								            '<td>' + bucket.name + '</td>' +
							 | 
						|
								            '</tr>' +
							 | 
						|
								            '<tr>' +
							 | 
						|
								            '<td><strong>Created:</strong></td>' +
							 | 
						|
								            '<td>' + formatDate(bucket.created_at) + '</td>' +
							 | 
						|
								            '</tr>' +
							 | 
						|
								            '<tr>' +
							 | 
						|
								            '<td><strong>Last Modified:</strong></td>' +
							 | 
						|
								            '<td>' + formatDate(bucket.last_modified) + '</td>' +
							 | 
						|
								            '</tr>' +
							 | 
						|
								            '<tr>' +
							 | 
						|
								            '<td><strong>Total Size:</strong></td>' +
							 | 
						|
								            '<td>' + formatBytes(bucket.size) + '</td>' +
							 | 
						|
								            '</tr>' +
							 | 
						|
								            '<tr>' +
							 | 
						|
								            '<td><strong>Object Count:</strong></td>' +
							 | 
						|
								            '<td>' + bucket.object_count + '</td>' +
							 | 
						|
								            '</tr>' +
							 | 
						|
								            '</table>' +
							 | 
						|
								            '</div>' +
							 | 
						|
								            '<div class="col-md-6">' +
							 | 
						|
								            '<h6><i class="fas fa-cogs me-2"></i>Configuration</h6>' +
							 | 
						|
								            '<table class="table table-sm">' +
							 | 
						|
								            '<tr>' +
							 | 
						|
								            '<td><strong>Quota:</strong></td>' +
							 | 
						|
								            '<td>' +
							 | 
						|
								            (bucket.quota_enabled ? 
							 | 
						|
								                '<span class="badge bg-success">' + formatBytes(bucket.quota) + '</span>' : 
							 | 
						|
								                '<span class="badge bg-secondary">Disabled</span>'
							 | 
						|
								            ) +
							 | 
						|
								            '</td>' +
							 | 
						|
								            '</tr>' +
							 | 
						|
								            '<tr>' +
							 | 
						|
								            '<td><strong>Versioning:</strong></td>' +
							 | 
						|
								            '<td>' +
							 | 
						|
								            (bucket.versioning_enabled ? 
							 | 
						|
								                '<span class="badge bg-success"><i class="fas fa-check me-1"></i>Enabled</span>' : 
							 | 
						|
								                '<span class="badge bg-secondary"><i class="fas fa-times me-1"></i>Disabled</span>'
							 | 
						|
								            ) +
							 | 
						|
								            '</td>' +
							 | 
						|
								            '</tr>' +
							 | 
						|
								            '<tr>' +
							 | 
						|
								            '<td><strong>Object Lock:</strong></td>' +
							 | 
						|
								            '<td>' +
							 | 
						|
								            (bucket.object_lock_enabled ? 
							 | 
						|
								                '<span class="badge bg-warning"><i class="fas fa-lock me-1"></i>Enabled</span>' +
							 | 
						|
								                '<br><small class="text-muted">' + bucket.object_lock_mode + ' • ' + bucket.object_lock_duration + ' days</small>' : 
							 | 
						|
								                '<span class="badge bg-secondary"><i class="fas fa-unlock me-1"></i>Disabled</span>'
							 | 
						|
								            ) +
							 | 
						|
								            '</td>' +
							 | 
						|
								            '</tr>' +
							 | 
						|
								            '</table>' +
							 | 
						|
								            '</div>' +
							 | 
						|
								            '</div>' +
							 | 
						|
								            '<hr>' +
							 | 
						|
								            '<div class="row">' +
							 | 
						|
								            '<div class="col-12">' +
							 | 
						|
								            '<h6><i class="fas fa-list me-2"></i>Objects (' + objects.length + ')</h6>' +
							 | 
						|
								            objectsTable +
							 | 
						|
								            '</div>' +
							 | 
						|
								            '</div>';
							 | 
						|
								        
							 | 
						|
								        document.getElementById('bucketDetailsContent').innerHTML = content;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    function exportBucketList() {
							 | 
						|
								        // Simple CSV export
							 | 
						|
								        const buckets = Array.from(document.querySelectorAll('#bucketsTable tbody tr')).map(row => {
							 | 
						|
								            const cells = row.querySelectorAll('td');
							 | 
						|
								            if (cells.length > 1) {
							 | 
						|
								                return {
							 | 
						|
								                    name: cells[0].textContent.trim(),
							 | 
						|
								                    created: cells[1].textContent.trim(),
							 | 
						|
								                    objects: cells[2].textContent.trim(),
							 | 
						|
								                    size: cells[3].textContent.trim(),
							 | 
						|
								                    quota: cells[4].textContent.trim(),
							 | 
						|
								                    versioning: cells[5].textContent.trim(),
							 | 
						|
								                    objectLock: cells[6].textContent.trim()
							 | 
						|
								                };
							 | 
						|
								            }
							 | 
						|
								            return null;
							 | 
						|
								        }).filter(bucket => bucket !== null);
							 | 
						|
								
							 | 
						|
								        const csvContent = "data:text/csv;charset=utf-8," + 
							 | 
						|
								            "Name,Created,Objects,Size,Quota,Versioning,Object Lock\n" +
							 | 
						|
								            buckets.map(b => '"' + b.name + '","' + b.created + '","' + b.objects + '","' + b.size + '","' + b.quota + '","' + b.versioning + '","' + b.objectLock + '"').join("\n");
							 | 
						|
								
							 | 
						|
								        const encodedUri = encodeURI(csvContent);
							 | 
						|
								        const link = document.createElement("a");
							 | 
						|
								        link.setAttribute("href", encodedUri);
							 | 
						|
								        link.setAttribute("download", "buckets.csv");
							 | 
						|
								        document.body.appendChild(link);
							 | 
						|
								        link.click();
							 | 
						|
								        document.body.removeChild(link);
							 | 
						|
								    }
							 | 
						|
								    </script>
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Helper functions for template
							 | 
						|
								func getQuotaStatusColor(used, quota int64, enabled bool) string {
							 | 
						|
								    if !enabled || quota <= 0 {
							 | 
						|
								        return "secondary"
							 | 
						|
								    }
							 | 
						|
								    
							 | 
						|
								    percentage := float64(used) / float64(quota) * 100
							 | 
						|
								    if percentage >= 90 {
							 | 
						|
								        return "danger"
							 | 
						|
								    } else if percentage >= 75 {
							 | 
						|
								        return "warning"
							 | 
						|
								    } else {
							 | 
						|
								        return "success"
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func getQuotaInMB(quotaBytes int64) int64 {
							 | 
						|
								    if quotaBytes < 0 {
							 | 
						|
								        quotaBytes = -quotaBytes // Handle disabled quotas (negative values)
							 | 
						|
								    }
							 | 
						|
								    return quotaBytes / (1024 * 1024)
							 | 
						|
								} 
							 |