aboutsummaryrefslogtreecommitdiff
path: root/weed/admin/view/app/s3_buckets_templ.go
diff options
context:
space:
mode:
Diffstat (limited to 'weed/admin/view/app/s3_buckets_templ.go')
-rw-r--r--weed/admin/view/app/s3_buckets_templ.go22
1 files changed, 11 insertions, 11 deletions
diff --git a/weed/admin/view/app/s3_buckets_templ.go b/weed/admin/view/app/s3_buckets_templ.go
index 0912a51c5..6edb5d371 100644
--- a/weed/admin/view/app/s3_buckets_templ.go
+++ b/weed/admin/view/app/s3_buckets_templ.go
@@ -290,27 +290,27 @@ func S3Buckets(data dash.S3BucketsData) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" class=\"btn btn-outline-success btn-sm\" title=\"Browse Files\"><i class=\"fas fa-folder-open\"></i></a> <a href=\"")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" 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=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- var templ_7745c5c3_Var17 templ.SafeURL
- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(fmt.Sprintf("/s3/buckets/%s", bucket.Name)))
+ var templ_7745c5c3_Var17 string
+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(bucket.Name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 190, Col: 118}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 192, Col: 89}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\" class=\"btn btn-outline-primary btn-sm\" title=\"View Details\"><i class=\"fas fa-eye\"></i></a> <button type=\"button\" class=\"btn btn-outline-warning btn-sm quota-btn\" data-bucket-name=\"")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\" 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=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(bucket.Name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 197, Col: 89}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 198, Col: 89}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
@@ -323,7 +323,7 @@ func S3Buckets(data dash.S3BucketsData) templ.Component {
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", getQuotaInMB(bucket.Quota)))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 198, Col: 125}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 199, Col: 125}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
@@ -336,7 +336,7 @@ func S3Buckets(data dash.S3BucketsData) templ.Component {
var templ_7745c5c3_Var20 string
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%t", bucket.QuotaEnabled))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 199, Col: 118}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 200, Col: 118}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
if templ_7745c5c3_Err != nil {
@@ -349,7 +349,7 @@ func S3Buckets(data dash.S3BucketsData) templ.Component {
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(bucket.Name)
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 205, Col: 89}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 206, Col: 89}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
@@ -373,13 +373,13 @@ func S3Buckets(data dash.S3BucketsData) templ.Component {
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(data.LastUpdated.Format("2006-01-02 15:04:05"))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 242, Col: 81}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/s3_buckets.templ`, Line: 243, Col: 81}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</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\"><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 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><!-- JavaScript for bucket management --><script>\n document.addEventListener('DOMContentLoaded', function() {\n const quotaCheckbox = document.getElementById('enableQuota');\n const quotaSettings = document.getElementById('quotaSettings');\n const versioningCheckbox = document.getElementById('enableVersioning');\n const objectLockCheckbox = document.getElementById('enableObjectLock');\n const objectLockSettings = document.getElementById('objectLockSettings');\n const createBucketForm = document.getElementById('createBucketForm');\n\n // Toggle quota settings\n quotaCheckbox.addEventListener('change', function() {\n quotaSettings.style.display = this.checked ? 'block' : 'none';\n });\n\n // Toggle object lock settings and automatically enable versioning\n objectLockCheckbox.addEventListener('change', function() {\n objectLockSettings.style.display = this.checked ? 'block' : 'none';\n if (this.checked) {\n versioningCheckbox.checked = true;\n versioningCheckbox.disabled = true;\n } else {\n versioningCheckbox.disabled = false;\n }\n });\n\n // Handle form submission\n createBucketForm.addEventListener('submit', function(e) {\n e.preventDefault();\n \n const formData = new FormData(this);\n const data = {\n name: formData.get('name'),\n region: formData.get('region') || '',\n quota_size: quotaCheckbox.checked ? parseInt(formData.get('quota_size')) || 0 : 0,\n quota_unit: formData.get('quota_unit') || 'MB',\n quota_enabled: quotaCheckbox.checked,\n versioning_enabled: versioningCheckbox.checked,\n object_lock_enabled: objectLockCheckbox.checked,\n object_lock_mode: formData.get('object_lock_mode') || 'GOVERNANCE',\n object_lock_duration: objectLockCheckbox.checked ? parseInt(formData.get('object_lock_duration')) || 30 : 0\n };\n\n // Validate object lock settings\n if (data.object_lock_enabled && data.object_lock_duration <= 0) {\n alert('Please enter a valid retention duration for object lock.');\n return;\n }\n\n fetch('/api/s3/buckets', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(data)\n })\n .then(response => response.json())\n .then(data => {\n if (data.error) {\n alert('Error creating bucket: ' + data.error);\n } else {\n alert('Bucket created successfully!');\n location.reload();\n }\n })\n .catch(error => {\n console.error('Error:', error);\n alert('Error creating bucket: ' + error.message);\n });\n });\n\n // Handle delete bucket\n document.querySelectorAll('.delete-bucket-btn').forEach(button => {\n button.addEventListener('click', function() {\n const bucketName = this.dataset.bucketName;\n document.getElementById('deleteBucketName').textContent = bucketName;\n window.currentBucketToDelete = bucketName;\n new bootstrap.Modal(document.getElementById('deleteBucketModal')).show();\n });\n });\n\n // Handle quota management\n document.querySelectorAll('.quota-btn').forEach(button => {\n button.addEventListener('click', function() {\n const bucketName = this.dataset.bucketName;\n const currentQuota = parseInt(this.dataset.currentQuota);\n const quotaEnabled = this.dataset.quotaEnabled === 'true';\n \n document.getElementById('quotaBucketName').value = bucketName;\n document.getElementById('quotaEnabled').checked = quotaEnabled;\n document.getElementById('quotaSizeMB').value = currentQuota;\n \n // Toggle quota size settings\n document.getElementById('quotaSizeSettings').style.display = quotaEnabled ? 'block' : 'none';\n \n window.currentBucketToUpdate = bucketName;\n new bootstrap.Modal(document.getElementById('manageQuotaModal')).show();\n });\n });\n\n // Handle quota form submission\n document.getElementById('quotaForm').addEventListener('submit', function(e) {\n e.preventDefault();\n \n const formData = new FormData(this);\n const enabled = document.getElementById('quotaEnabled').checked;\n const data = {\n quota_size: enabled ? parseInt(formData.get('quota_size')) || 0 : 0,\n quota_unit: formData.get('quota_unit') || 'MB',\n quota_enabled: enabled\n };\n\n fetch(`/api/s3/buckets/${window.currentBucketToUpdate}/quota`, {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(data)\n })\n .then(response => response.json())\n .then(data => {\n if (data.error) {\n alert('Error updating quota: ' + data.error);\n } else {\n alert('Quota updated successfully!');\n location.reload();\n }\n })\n .catch(error => {\n console.error('Error:', error);\n alert('Error updating quota: ' + error.message);\n });\n });\n\n // Handle quota enabled checkbox\n document.getElementById('quotaEnabled').addEventListener('change', function() {\n document.getElementById('quotaSizeSettings').style.display = this.checked ? 'block' : 'none';\n });\n });\n\n function deleteBucket() {\n const bucketName = window.currentBucketToDelete;\n if (!bucketName) return;\n\n fetch(`/api/s3/buckets/${bucketName}`, {\n method: 'DELETE'\n })\n .then(response => response.json())\n .then(data => {\n if (data.error) {\n alert('Error deleting bucket: ' + data.error);\n } else {\n alert('Bucket deleted successfully!');\n location.reload();\n }\n })\n .catch(error => {\n console.error('Error:', error);\n alert('Error deleting bucket: ' + error.message);\n });\n }\n\n function exportBucketList() {\n // Simple CSV export\n const buckets = Array.from(document.querySelectorAll('#bucketsTable tbody tr')).map(row => {\n const cells = row.querySelectorAll('td');\n if (cells.length > 1) {\n return {\n name: cells[0].textContent.trim(),\n created: cells[1].textContent.trim(),\n objects: cells[2].textContent.trim(),\n size: cells[3].textContent.trim(),\n quota: cells[4].textContent.trim(),\n versioning: cells[5].textContent.trim(),\n objectLock: cells[6].textContent.trim()\n };\n }\n return null;\n }).filter(bucket => bucket !== null);\n\n const csvContent = \"data:text/csv;charset=utf-8,\" + \n \"Name,Created,Objects,Size,Quota,Versioning,Object Lock\\n\" +\n buckets.map(b => `\"${b.name}\",\"${b.created}\",\"${b.objects}\",\"${b.size}\",\"${b.quota}\",\"${b.versioning}\",\"${b.objectLock}\"`).join(\"\\n\");\n\n const encodedUri = encodeURI(csvContent);\n const link = document.createElement(\"a\");\n link.setAttribute(\"href\", encodedUri);\n link.setAttribute(\"download\", \"buckets.csv\");\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n </script>")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</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\"><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 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>\n document.addEventListener('DOMContentLoaded', function() {\n const quotaCheckbox = document.getElementById('enableQuota');\n const quotaSettings = document.getElementById('quotaSettings');\n const versioningCheckbox = document.getElementById('enableVersioning');\n const objectLockCheckbox = document.getElementById('enableObjectLock');\n const objectLockSettings = document.getElementById('objectLockSettings');\n const createBucketForm = document.getElementById('createBucketForm');\n\n // Toggle quota settings\n quotaCheckbox.addEventListener('change', function() {\n quotaSettings.style.display = this.checked ? 'block' : 'none';\n });\n\n // Toggle object lock settings and automatically enable versioning\n objectLockCheckbox.addEventListener('change', function() {\n objectLockSettings.style.display = this.checked ? 'block' : 'none';\n if (this.checked) {\n versioningCheckbox.checked = true;\n versioningCheckbox.disabled = true;\n } else {\n versioningCheckbox.disabled = false;\n }\n });\n\n // Handle form submission\n createBucketForm.addEventListener('submit', function(e) {\n e.preventDefault();\n \n const formData = new FormData(this);\n const data = {\n name: formData.get('name'),\n region: formData.get('region') || '',\n quota_size: quotaCheckbox.checked ? parseInt(formData.get('quota_size')) || 0 : 0,\n quota_unit: formData.get('quota_unit') || 'MB',\n quota_enabled: quotaCheckbox.checked,\n versioning_enabled: versioningCheckbox.checked,\n object_lock_enabled: objectLockCheckbox.checked,\n object_lock_mode: formData.get('object_lock_mode') || 'GOVERNANCE',\n object_lock_duration: objectLockCheckbox.checked ? parseInt(formData.get('object_lock_duration')) || 30 : 0\n };\n\n // Validate object lock settings\n if (data.object_lock_enabled && data.object_lock_duration <= 0) {\n alert('Please enter a valid retention duration for object lock.');\n return;\n }\n\n fetch('/api/s3/buckets', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(data)\n })\n .then(response => response.json())\n .then(data => {\n if (data.error) {\n alert('Error creating bucket: ' + data.error);\n } else {\n alert('Bucket created successfully!');\n // Properly close the modal before reloading\n const createModal = bootstrap.Modal.getInstance(document.getElementById('createBucketModal'));\n if (createModal) {\n createModal.hide();\n }\n setTimeout(() => location.reload(), 500);\n }\n })\n .catch(error => {\n console.error('Error:', error);\n alert('Error creating bucket: ' + error.message);\n });\n });\n\n // Handle delete bucket\n let deleteModalInstance = null;\n document.querySelectorAll('.delete-bucket-btn').forEach(button => {\n button.addEventListener('click', function() {\n const bucketName = this.dataset.bucketName;\n document.getElementById('deleteBucketName').textContent = bucketName;\n window.currentBucketToDelete = bucketName;\n \n // Dispose of existing modal instance if it exists\n if (deleteModalInstance) {\n deleteModalInstance.dispose();\n }\n \n // Create new modal instance\n deleteModalInstance = new bootstrap.Modal(document.getElementById('deleteBucketModal'));\n deleteModalInstance.show();\n });\n });\n\n // Add event listener to properly dispose of delete modal when hidden\n document.getElementById('deleteBucketModal').addEventListener('hidden.bs.modal', function() {\n if (deleteModalInstance) {\n deleteModalInstance.dispose();\n deleteModalInstance = null;\n }\n // Force remove any remaining backdrops\n document.querySelectorAll('.modal-backdrop').forEach(backdrop => {\n backdrop.remove();\n });\n // Ensure body classes are removed\n document.body.classList.remove('modal-open');\n document.body.style.removeProperty('padding-right');\n });\n\n // Handle quota management\n let quotaModalInstance = null;\n document.querySelectorAll('.quota-btn').forEach(button => {\n button.addEventListener('click', function() {\n const bucketName = this.dataset.bucketName;\n const currentQuota = parseInt(this.dataset.currentQuota);\n const quotaEnabled = this.dataset.quotaEnabled === 'true';\n \n document.getElementById('quotaBucketName').value = bucketName;\n document.getElementById('quotaEnabled').checked = quotaEnabled;\n document.getElementById('quotaSizeMB').value = currentQuota;\n \n // Toggle quota size settings\n document.getElementById('quotaSizeSettings').style.display = quotaEnabled ? 'block' : 'none';\n \n window.currentBucketToUpdate = bucketName;\n \n // Dispose of existing modal instance if it exists\n if (quotaModalInstance) {\n quotaModalInstance.dispose();\n }\n \n // Create new modal instance\n quotaModalInstance = new bootstrap.Modal(document.getElementById('manageQuotaModal'));\n quotaModalInstance.show();\n });\n });\n\n // Add event listener to properly dispose of quota modal when hidden\n document.getElementById('manageQuotaModal').addEventListener('hidden.bs.modal', function() {\n if (quotaModalInstance) {\n quotaModalInstance.dispose();\n quotaModalInstance = null;\n }\n // Force remove any remaining backdrops\n document.querySelectorAll('.modal-backdrop').forEach(backdrop => {\n backdrop.remove();\n });\n // Ensure body classes are removed\n document.body.classList.remove('modal-open');\n document.body.style.removeProperty('padding-right');\n });\n\n // Handle quota form submission\n document.getElementById('quotaForm').addEventListener('submit', function(e) {\n e.preventDefault();\n \n const formData = new FormData(this);\n const enabled = document.getElementById('quotaEnabled').checked;\n const data = {\n quota_size: enabled ? parseInt(formData.get('quota_size')) || 0 : 0,\n quota_unit: formData.get('quota_unit') || 'MB',\n quota_enabled: enabled\n };\n\n fetch(`/api/s3/buckets/${window.currentBucketToUpdate}/quota`, {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(data)\n })\n .then(response => response.json())\n .then(data => {\n if (data.error) {\n alert('Error updating quota: ' + data.error);\n } else {\n alert('Quota updated successfully!');\n // Properly close the modal before reloading\n if (quotaModalInstance) {\n quotaModalInstance.hide();\n }\n setTimeout(() => location.reload(), 500);\n }\n })\n .catch(error => {\n console.error('Error:', error);\n alert('Error updating quota: ' + error.message);\n });\n });\n\n // Handle quota enabled checkbox\n document.getElementById('quotaEnabled').addEventListener('change', function() {\n document.getElementById('quotaSizeSettings').style.display = this.checked ? 'block' : 'none';\n });\n\n // Handle view details button\n let detailsModalInstance = null;\n document.querySelectorAll('.view-details-btn').forEach(button => {\n button.addEventListener('click', function() {\n const bucketName = this.dataset.bucketName;\n \n // Update modal title\n document.getElementById('bucketDetailsModalLabel').innerHTML = \n '<i class=\"fas fa-cube me-2\"></i>Bucket Details - ' + bucketName;\n \n // Show loading spinner\n document.getElementById('bucketDetailsContent').innerHTML = \n '<div class=\"text-center py-4\">' +\n '<div class=\"spinner-border text-primary\" role=\"status\">' +\n '<span class=\"visually-hidden\">Loading...</span>' +\n '</div>' +\n '<div class=\"mt-2\">Loading bucket details...</div>' +\n '</div>';\n \n // Dispose of existing modal instance if it exists\n if (detailsModalInstance) {\n detailsModalInstance.dispose();\n }\n \n // Create new modal instance\n detailsModalInstance = new bootstrap.Modal(document.getElementById('bucketDetailsModal'));\n detailsModalInstance.show();\n \n // Fetch bucket details\n fetch('/api/s3/buckets/' + bucketName)\n .then(response => response.json())\n .then(data => {\n if (data.error) {\n document.getElementById('bucketDetailsContent').innerHTML = \n '<div class=\"alert alert-danger\">' +\n '<i class=\"fas fa-exclamation-triangle me-2\"></i>' +\n 'Error loading bucket details: ' + data.error +\n '</div>';\n } else {\n displayBucketDetails(data);\n }\n })\n .catch(error => {\n console.error('Error fetching bucket details:', error);\n document.getElementById('bucketDetailsContent').innerHTML = \n '<div class=\"alert alert-danger\">' +\n '<i class=\"fas fa-exclamation-triangle me-2\"></i>' +\n 'Error loading bucket details: ' + error.message +\n '</div>';\n });\n });\n });\n\n // Add event listener to properly dispose of details modal when hidden\n document.getElementById('bucketDetailsModal').addEventListener('hidden.bs.modal', function() {\n if (detailsModalInstance) {\n detailsModalInstance.dispose();\n detailsModalInstance = null;\n }\n // Force remove any remaining backdrops\n document.querySelectorAll('.modal-backdrop').forEach(backdrop => {\n backdrop.remove();\n });\n // Ensure body classes are removed\n document.body.classList.remove('modal-open');\n document.body.style.removeProperty('padding-right');\n });\n });\n\n function deleteBucket() {\n const bucketName = window.currentBucketToDelete;\n if (!bucketName) return;\n\n fetch(`/api/s3/buckets/${bucketName}`, {\n method: 'DELETE'\n })\n .then(response => response.json())\n .then(data => {\n if (data.error) {\n alert('Error deleting bucket: ' + data.error);\n } else {\n alert('Bucket deleted successfully!');\n // Properly close the modal before reloading\n if (deleteModalInstance) {\n deleteModalInstance.hide();\n }\n setTimeout(() => location.reload(), 500);\n }\n })\n .catch(error => {\n console.error('Error:', error);\n alert('Error deleting bucket: ' + error.message);\n });\n }\n\n function displayBucketDetails(data) {\n const bucket = data.bucket;\n const objects = data.objects || [];\n \n // Helper function to format bytes\n function formatBytes(bytes) {\n if (bytes === 0) return '0 Bytes';\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n }\n \n // Helper function to format date\n function formatDate(dateString) {\n const date = new Date(dateString);\n return date.toLocaleString();\n }\n \n // Generate objects table\n let objectsTable = '';\n if (objects.length > 0) {\n objectsTable = '<div class=\"table-responsive\">' +\n '<table class=\"table table-sm table-striped\">' +\n '<thead>' +\n '<tr>' +\n '<th>Object Key</th>' +\n '<th>Size</th>' +\n '<th>Last Modified</th>' +\n '<th>Storage Class</th>' +\n '</tr>' +\n '</thead>' +\n '<tbody>' +\n objects.map(obj => \n '<tr>' +\n '<td><i class=\"fas fa-file me-1\"></i>' + obj.key + '</td>' +\n '<td>' + formatBytes(obj.size) + '</td>' +\n '<td>' + formatDate(obj.last_modified) + '</td>' +\n '<td><span class=\"badge bg-primary\">' + obj.storage_class + '</span></td>' +\n '</tr>'\n ).join('') +\n '</tbody>' +\n '</table>' +\n '</div>';\n } else {\n objectsTable = '<div class=\"text-center py-4 text-muted\">' +\n '<i class=\"fas fa-file fa-3x mb-3\"></i>' +\n '<div>No objects found in this bucket</div>' +\n '</div>';\n }\n \n const content = '<div class=\"row\">' +\n '<div class=\"col-md-6\">' +\n '<h6><i class=\"fas fa-info-circle me-2\"></i>Bucket Information</h6>' +\n '<table class=\"table table-sm\">' +\n '<tr>' +\n '<td><strong>Name:</strong></td>' +\n '<td>' + bucket.name + '</td>' +\n '</tr>' +\n '<tr>' +\n '<td><strong>Created:</strong></td>' +\n '<td>' + formatDate(bucket.created_at) + '</td>' +\n '</tr>' +\n '<tr>' +\n '<td><strong>Last Modified:</strong></td>' +\n '<td>' + formatDate(bucket.last_modified) + '</td>' +\n '</tr>' +\n '<tr>' +\n '<td><strong>Total Size:</strong></td>' +\n '<td>' + formatBytes(bucket.size) + '</td>' +\n '</tr>' +\n '<tr>' +\n '<td><strong>Object Count:</strong></td>' +\n '<td>' + bucket.object_count + '</td>' +\n '</tr>' +\n '</table>' +\n '</div>' +\n '<div class=\"col-md-6\">' +\n '<h6><i class=\"fas fa-cogs me-2\"></i>Configuration</h6>' +\n '<table class=\"table table-sm\">' +\n '<tr>' +\n '<td><strong>Quota:</strong></td>' +\n '<td>' +\n (bucket.quota_enabled ? \n '<span class=\"badge bg-success\">' + formatBytes(bucket.quota) + '</span>' : \n '<span class=\"badge bg-secondary\">Disabled</span>'\n ) +\n '</td>' +\n '</tr>' +\n '<tr>' +\n '<td><strong>Versioning:</strong></td>' +\n '<td>' +\n (bucket.versioning_enabled ? \n '<span class=\"badge bg-success\"><i class=\"fas fa-check me-1\"></i>Enabled</span>' : \n '<span class=\"badge bg-secondary\"><i class=\"fas fa-times me-1\"></i>Disabled</span>'\n ) +\n '</td>' +\n '</tr>' +\n '<tr>' +\n '<td><strong>Object Lock:</strong></td>' +\n '<td>' +\n (bucket.object_lock_enabled ? \n '<span class=\"badge bg-warning\"><i class=\"fas fa-lock me-1\"></i>Enabled</span>' +\n '<br><small class=\"text-muted\">' + bucket.object_lock_mode + ' • ' + bucket.object_lock_duration + ' days</small>' : \n '<span class=\"badge bg-secondary\"><i class=\"fas fa-unlock me-1\"></i>Disabled</span>'\n ) +\n '</td>' +\n '</tr>' +\n '</table>' +\n '</div>' +\n '</div>' +\n '<hr>' +\n '<div class=\"row\">' +\n '<div class=\"col-12\">' +\n '<h6><i class=\"fas fa-list me-2\"></i>Objects (' + objects.length + ')</h6>' +\n objectsTable +\n '</div>' +\n '</div>';\n \n document.getElementById('bucketDetailsContent').innerHTML = content;\n }\n\n function exportBucketList() {\n // Simple CSV export\n const buckets = Array.from(document.querySelectorAll('#bucketsTable tbody tr')).map(row => {\n const cells = row.querySelectorAll('td');\n if (cells.length > 1) {\n return {\n name: cells[0].textContent.trim(),\n created: cells[1].textContent.trim(),\n objects: cells[2].textContent.trim(),\n size: cells[3].textContent.trim(),\n quota: cells[4].textContent.trim(),\n versioning: cells[5].textContent.trim(),\n objectLock: cells[6].textContent.trim()\n };\n }\n return null;\n }).filter(bucket => bucket !== null);\n\n const csvContent = \"data:text/csv;charset=utf-8,\" + \n \"Name,Created,Objects,Size,Quota,Versioning,Object Lock\\n\" +\n buckets.map(b => '\"' + b.name + '\",\"' + b.created + '\",\"' + b.objects + '\",\"' + b.size + '\",\"' + b.quota + '\",\"' + b.versioning + '\",\"' + b.objectLock + '\"').join(\"\\n\");\n\n const encodedUri = encodeURI(csvContent);\n const link = document.createElement(\"a\");\n link.setAttribute(\"href\", encodedUri);\n link.setAttribute(\"download\", \"buckets.csv\");\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n }\n </script>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}