From 74c7b10bc710243d9d9fb470f9da0417ef16b004 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 25 Jan 2026 23:09:14 -0800 Subject: [PATCH 1/2] Fix Chrome dialog auto-dismiss with Bootstrap modals - Add modal-alerts.js library with Bootstrap modal replacements - Replace all 15 confirm() calls with showConfirm/showDeleteConfirm - Auto-override window.alert() for all alert() calls - Fixes Chrome 132+ aggressively blocking native dialogs --- weed/admin/static/js/modal-alerts.js | 302 ++++++++++++++++++ weed/admin/view/app/cluster_ec_shards.templ | 4 +- .../admin/view/app/cluster_ec_shards_templ.go | 2 +- weed/admin/view/app/cluster_ec_volumes.templ | 4 +- .../view/app/cluster_ec_volumes_templ.go | 2 +- weed/admin/view/app/collection_details.templ | 4 +- .../view/app/collection_details_templ.go | 2 +- weed/admin/view/app/file_browser.templ | 4 +- weed/admin/view/app/file_browser_templ.go | 2 +- weed/admin/view/app/maintenance_config.templ | 4 +- .../view/app/maintenance_config_schema.templ | 4 +- .../app/maintenance_config_schema_templ.go | 2 +- .../view/app/maintenance_config_templ.go | 2 +- weed/admin/view/app/maintenance_workers.templ | 4 +- .../view/app/maintenance_workers_templ.go | 2 +- weed/admin/view/app/object_store_users.templ | 10 +- .../view/app/object_store_users_templ.go | 2 +- weed/admin/view/app/policies.templ | 6 +- weed/admin/view/app/policies_templ.go | 2 +- weed/admin/view/app/service_accounts.templ | 4 +- weed/admin/view/app/service_accounts_templ.go | 2 +- weed/admin/view/app/task_config.templ | 4 +- weed/admin/view/app/task_config_schema.templ | 4 +- .../view/app/task_config_schema_templ.go | 2 +- weed/admin/view/app/task_config_templ.go | 2 +- weed/admin/view/app/task_config_templ.templ | 4 +- .../admin/view/app/task_config_templ_templ.go | 2 +- weed/admin/view/app/task_detail.templ | 4 +- weed/admin/view/app/task_detail_templ.go | 2 +- weed/admin/view/layout/layout.templ | 2 + weed/admin/view/layout/layout_templ.go | 8 +- 31 files changed, 357 insertions(+), 47 deletions(-) create mode 100644 weed/admin/static/js/modal-alerts.js diff --git a/weed/admin/static/js/modal-alerts.js b/weed/admin/static/js/modal-alerts.js new file mode 100644 index 000000000..612bc1961 --- /dev/null +++ b/weed/admin/static/js/modal-alerts.js @@ -0,0 +1,302 @@ +/** + * Modal Alerts - Bootstrap Modal replacement for native alert() and confirm() + * Fixes Chrome auto-dismiss issue with native dialogs + * + * Usage: + * showAlert('Message', 'success'); + * showConfirm('Delete this?', function() { }); + */ + +(function() { + 'use strict'; + + // Create and inject modal HTML into page if not already present + function ensureModalsExist() { + if (document.getElementById('globalAlertModal')) { + return; // Already exists + } + + const modalsHTML = ` + + + + + + + + + `; + + // Inject modals at end of body + document.body.insertAdjacentHTML('beforeend', modalsHTML); + } + + /** + * Show an alert message using Bootstrap modal + * @param {string} message - The message to display + * @param {string} type - Type: 'success', 'error', 'warning', 'info' (default: 'info') + * @param {string} title - Optional custom title + */ + window.showAlert = function(message, type, title) { + ensureModalsExist(); + + const modal = document.getElementById('globalAlertModal'); + const header = document.getElementById('globalAlertModalHeader'); + const titleEl = document.getElementById('globalAlertModalTitle'); + const bodyEl = document.getElementById('globalAlertModalBody'); + const iconEl = document.getElementById('globalAlertModalIcon'); + + // Configuration for different types + const types = { + 'success': { + title: 'Success', + icon: 'fa-check-circle', + headerClass: 'bg-success text-white', + btnClose: 'btn-close-white' + }, + 'error': { + title: 'Error', + icon: 'fa-exclamation-triangle', + headerClass: 'bg-danger text-white', + btnClose: 'btn-close-white' + }, + 'warning': { + title: 'Warning', + icon: 'fa-exclamation-circle', + headerClass: 'bg-warning text-dark', + btnClose: '' + }, + 'info': { + title: 'Notice', + icon: 'fa-info-circle', + headerClass: 'bg-info text-white', + btnClose: 'btn-close-white' + } + }; + + const config = types[type] || types['info']; + + // Update header styling + header.className = 'modal-header ' + config.headerClass; + const closeBtn = header.querySelector('.btn-close'); + closeBtn.className = 'btn-close ' + config.btnClose; + + // Update icon + iconEl.className = 'fas ' + config.icon + ' me-2'; + + // Update title + titleEl.textContent = title || config.title; + + // Update body - support HTML or text + if (message.includes('<')) { + bodyEl.innerHTML = message; + } else { + bodyEl.innerHTML = '

' + escapeHtml(message) + '

'; + } + + // Show modal + const bsModal = new bootstrap.Modal(modal); + bsModal.show(); + }; + + /** + * Show a confirmation dialog using Bootstrap modal + * @param {string} message - The confirmation message + * @param {function} onConfirm - Callback function if user confirms + * @param {function} onCancel - Optional callback function if user cancels + * @param {string} title - Optional custom title + */ + window.showConfirm = function(message, onConfirm, onCancel, title) { + ensureModalsExist(); + + const modalEl = document.getElementById('globalConfirmModal'); + const bodyEl = document.getElementById('globalConfirmModalBody'); + const titleEl = document.getElementById('globalConfirmModalLabel').querySelector('span'); + const okBtn = document.getElementById('globalConfirmOkBtn'); + const cancelBtn = document.getElementById('globalConfirmCancelBtn'); + + // Set title if provided + if (title) { + if (titleEl) { + titleEl.textContent = title; + } else { + document.getElementById('globalConfirmModalLabel').insertAdjacentHTML('beforeend', '' + escapeHtml(title) + ''); + } + } + + // Set message + if (message.includes('<')) { + bodyEl.innerHTML = message; + } else { + bodyEl.innerHTML = '

' + escapeHtml(message) + '

'; + } + + // Remove old event listeners by cloning buttons + const newOkBtn = okBtn.cloneNode(true); + const newCancelBtn = cancelBtn.cloneNode(true); + okBtn.parentNode.replaceChild(newOkBtn, okBtn); + cancelBtn.parentNode.replaceChild(newCancelBtn, cancelBtn); + + const modal = new bootstrap.Modal(modalEl); + + // Add event listeners + newOkBtn.addEventListener('click', function() { + modal.hide(); + if (typeof onConfirm === 'function') { + onConfirm(); + } + }); + + newCancelBtn.addEventListener('click', function() { + modal.hide(); + if (typeof onCancel === 'function') { + onCancel(); + } + }); + + modal.show(); + }; + + /** + * Show a delete confirmation dialog + * @param {string} itemName - Name of the item to delete + * @param {function} onConfirm - Callback function if user confirms deletion + * @param {string} message - Optional custom message (default: "Are you sure you want to delete this item?") + */ + window.showDeleteConfirm = function(itemName, onConfirm, message) { + ensureModalsExist(); + + const modalEl = document.getElementById('globalDeleteModal'); + const messageEl = document.getElementById('globalDeleteModalMessage'); + const itemNameEl = document.getElementById('globalDeleteModalItemName'); + const confirmBtn = document.getElementById('globalDeleteConfirmBtn'); + + // Set custom message if provided + if (message) { + messageEl.textContent = message; + } else { + messageEl.textContent = 'Are you sure you want to delete this item?'; + } + + // Set item name + itemNameEl.textContent = itemName; + + // Remove old event listener by cloning button + const newConfirmBtn = confirmBtn.cloneNode(true); + confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn); + + const modal = new bootstrap.Modal(modalEl); + + // Add new event listener + newConfirmBtn.addEventListener('click', function() { + modal.hide(); + if (typeof onConfirm === 'function') { + onConfirm(); + } + }); + + modal.show(); + }; + + /** + * Escape HTML to prevent XSS + */ + function escapeHtml(text) { + const map = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + return text.replace(/[&<>"']/g, function(m) { return map[m]; }); + } + + // Auto-initialize on DOMContentLoaded + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', ensureModalsExist); + } else { + ensureModalsExist(); + } + + /** + * AUTOMATIC OVERRIDE of native alert() + * This makes ALL existing alert() calls automatically use Bootstrap modals + */ + window.alert = function(message) { + // Auto-detect message type from content + let type = 'info'; + const msgLower = (message || '').toLowerCase(); + + if (msgLower.includes('success') || msgLower.includes('created') || msgLower.includes('updated') || msgLower.includes('saved')) { + type = 'success'; + } else if (msgLower.includes('error') || msgLower.includes('failed') || msgLower.includes('invalid') || msgLower.includes('cannot')) { + type = 'error'; + } else if (msgLower.includes('warning') || msgLower.includes('please') || msgLower.includes('required')) { + type = 'warning'; + } + + showAlert(message, type); + }; + + console.log('Modal Alerts library loaded - native alert() overridden'); + console.log('For confirm(), use showConfirm() or showDeleteConfirm() instead of native confirm()'); +})(); diff --git a/weed/admin/view/app/cluster_ec_shards.templ b/weed/admin/view/app/cluster_ec_shards.templ index 19f6fd2d6..d7180f2bb 100644 --- a/weed/admin/view/app/cluster_ec_shards.templ +++ b/weed/admin/view/app/cluster_ec_shards.templ @@ -387,7 +387,7 @@ templ ClusterEcShards(data dash.ClusterEcShardsData) { // Get data from the button element (not the icon inside it) const button = event.target.closest('button'); const volumeId = button.getAttribute('data-volume-id'); - if (confirm(`Are you sure you want to repair missing shards for volume ${volumeId}?`)) { + showConfirm(`Are you sure you want to repair missing shards for volume ${volumeId}?`, function() { fetch(`/api/storage/volumes/${volumeId}/repair`, { method: 'POST', headers: { @@ -406,7 +406,7 @@ templ ClusterEcShards(data dash.ClusterEcShardsData) { .catch(error => { alert('Error: ' + error.message); }); - } + }); } } diff --git a/weed/admin/view/app/cluster_ec_shards_templ.go b/weed/admin/view/app/cluster_ec_shards_templ.go index b7c169d1e..31cf8f9bd 100644 --- a/weed/admin/view/app/cluster_ec_shards_templ.go +++ b/weed/admin/view/app/cluster_ec_shards_templ.go @@ -663,7 +663,7 @@ func ClusterEcShards(data dash.ClusterEcShardsData) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/cluster_ec_volumes.templ b/weed/admin/view/app/cluster_ec_volumes.templ index 6e94443ae..de0f32168 100644 --- a/weed/admin/view/app/cluster_ec_volumes.templ +++ b/weed/admin/view/app/cluster_ec_volumes.templ @@ -378,7 +378,7 @@ templ ClusterEcVolumes(data dash.ClusterEcVolumesData) { function repairVolume(event) { const volumeId = event.target.closest('button').getAttribute('data-volume-id'); - if (confirm(`Are you sure you want to repair missing shards for volume ${volumeId}?`)) { + showConfirm(`Are you sure you want to repair missing shards for volume ${volumeId}?`, function() { fetch(`/api/storage/ec-volumes/${volumeId}/repair`, { method: 'POST', headers: { @@ -402,7 +402,7 @@ templ ClusterEcVolumes(data dash.ClusterEcVolumesData) { .catch(error => { alert('Error: ' + error.message); }); - } + }); } } diff --git a/weed/admin/view/app/cluster_ec_volumes_templ.go b/weed/admin/view/app/cluster_ec_volumes_templ.go index 9103607a8..1c83c51ba 100644 --- a/weed/admin/view/app/cluster_ec_volumes_templ.go +++ b/weed/admin/view/app/cluster_ec_volumes_templ.go @@ -757,7 +757,7 @@ func ClusterEcVolumes(data dash.ClusterEcVolumesData) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/collection_details.templ b/weed/admin/view/app/collection_details.templ index 296839b93..46075613c 100644 --- a/weed/admin/view/app/collection_details.templ +++ b/weed/admin/view/app/collection_details.templ @@ -372,10 +372,10 @@ templ CollectionDetails(data dash.CollectionDetailsData) { // Repair EC Volume function repairEcVolume(event) { const volumeId = event.target.closest('button').getAttribute('data-volume-id'); - if (confirm(`Are you sure you want to repair missing shards for EC volume ${volumeId}?`)) { + showConfirm(`Are you sure you want to repair missing shards for EC volume ${volumeId}?`, function() { // TODO: Implement repair functionality alert('Repair functionality will be implemented soon.'); - } + }); } } \ No newline at end of file diff --git a/weed/admin/view/app/collection_details_templ.go b/weed/admin/view/app/collection_details_templ.go index f2ff0ab13..762f3c391 100644 --- a/weed/admin/view/app/collection_details_templ.go +++ b/weed/admin/view/app/collection_details_templ.go @@ -575,7 +575,7 @@ func CollectionDetails(data dash.CollectionDetailsData) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/file_browser.templ b/weed/admin/view/app/file_browser.templ index 6ae00b81b..a88f00488 100644 --- a/weed/admin/view/app/file_browser.templ +++ b/weed/admin/view/app/file_browser.templ @@ -365,9 +365,9 @@ templ FileBrowser(data dash.FileBrowserData) { showFileProperties(path); break; case 'delete': - if (confirm('Are you sure you want to delete "' + path + '"?')) { + showDeleteConfirm(path, function() { deleteFile(path); - } + }, 'Are you sure you want to delete this file?'); break; } }); diff --git a/weed/admin/view/app/file_browser_templ.go b/weed/admin/view/app/file_browser_templ.go index 4a66ff026..ca787195c 100644 --- a/weed/admin/view/app/file_browser_templ.go +++ b/weed/admin/view/app/file_browser_templ.go @@ -700,7 +700,7 @@ func FileBrowser(data dash.FileBrowserData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "\">
0%
Preparing upload...
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "\">
0%
Preparing upload...
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/maintenance_config.templ b/weed/admin/view/app/maintenance_config.templ index 65ef565af..4110486ba 100644 --- a/weed/admin/view/app/maintenance_config.templ +++ b/weed/admin/view/app/maintenance_config.templ @@ -251,7 +251,7 @@ templ MaintenanceConfig(data *maintenance.MaintenanceConfigData) { } function resetToDefaults() { - if (confirm('Are you sure you want to reset to default configuration? This will overwrite your current settings.')) { + showConfirm('Are you sure you want to reset to default configuration? This will overwrite your current settings.', function() { // Reset form to defaults (matching DefaultMaintenanceConfig values) document.getElementById('enabled').checked = false; document.getElementById('scanInterval').value = '30'; @@ -261,7 +261,7 @@ templ MaintenanceConfig(data *maintenance.MaintenanceConfigData) { document.getElementById('maxRetries').value = '3'; document.getElementById('retryDelay').value = '15'; document.getElementById('taskRetention').value = '7'; - } + }); } } \ No newline at end of file diff --git a/weed/admin/view/app/maintenance_config_schema.templ b/weed/admin/view/app/maintenance_config_schema.templ index ee89cab64..3e1d6ee1f 100644 --- a/weed/admin/view/app/maintenance_config_schema.templ +++ b/weed/admin/view/app/maintenance_config_schema.templ @@ -179,7 +179,7 @@ templ MaintenanceConfigSchema(data *maintenance.MaintenanceConfigData, schema *m } function resetToDefaults() { - if (confirm('Are you sure you want to reset to default configuration? This will overwrite your current settings.')) { + showConfirm('Are you sure you want to reset to default configuration? This will overwrite your current settings.', function() { fetch('/maintenance/config/defaults', { method: 'POST', headers: { @@ -199,7 +199,7 @@ templ MaintenanceConfigSchema(data *maintenance.MaintenanceConfigData, schema *m console.error('Error:', error); alert('Error resetting configuration: ' + error.message); }); - } + }); } } diff --git a/weed/admin/view/app/maintenance_config_schema_templ.go b/weed/admin/view/app/maintenance_config_schema_templ.go index b7046f3f9..b4d806fb6 100644 --- a/weed/admin/view/app/maintenance_config_schema_templ.go +++ b/weed/admin/view/app/maintenance_config_schema_templ.go @@ -46,7 +46,7 @@ func MaintenanceConfigSchema(data *maintenance.MaintenanceConfigData, schema *ma return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
Volume Vacuum

Reclaims disk space by removing deleted files from volumes.

Configure
Volume Balance

Redistributes volumes across servers to optimize storage utilization.

Configure
Erasure Coding

Converts volumes to erasure coded format for improved durability.

Configure
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "
Volume Vacuum

Reclaims disk space by removing deleted files from volumes.

Configure
Volume Balance

Redistributes volumes across servers to optimize storage utilization.

Configure
Erasure Coding

Converts volumes to erasure coded format for improved durability.

Configure
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/maintenance_config_templ.go b/weed/admin/view/app/maintenance_config_templ.go index 45e9b8ef1..75017b31d 100644 --- a/weed/admin/view/app/maintenance_config_templ.go +++ b/weed/admin/view/app/maintenance_config_templ.go @@ -273,7 +273,7 @@ func MaintenanceConfig(data *maintenance.MaintenanceConfigData) templ.Component if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/maintenance_workers.templ b/weed/admin/view/app/maintenance_workers.templ index 00748e550..eb95c0d55 100644 --- a/weed/admin/view/app/maintenance_workers.templ +++ b/weed/admin/view/app/maintenance_workers.templ @@ -302,7 +302,7 @@ templ MaintenanceWorkers(data *dash.MaintenanceWorkersData) { function pauseWorker(event) { const workerID = event.target.closest('button').getAttribute('data-worker-id'); - if (confirm('Are you sure you want to pause this worker?')) { + showConfirm('Are you sure you want to pause this worker?', function() { fetch('/api/maintenance/workers/' + workerID + '/pause', { method: 'POST' }) @@ -318,7 +318,7 @@ templ MaintenanceWorkers(data *dash.MaintenanceWorkersData) { console.error('Error pausing worker:', error); alert('Failed to pause worker'); }); - } + }); } function formatDuration(nanoseconds) { diff --git a/weed/admin/view/app/maintenance_workers_templ.go b/weed/admin/view/app/maintenance_workers_templ.go index f1fd13ebb..12378b532 100644 --- a/weed/admin/view/app/maintenance_workers_templ.go +++ b/weed/admin/view/app/maintenance_workers_templ.go @@ -390,7 +390,7 @@ func MaintenanceWorkers(data *dash.MaintenanceWorkersData) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "
Worker Details
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "
Worker Details
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/object_store_users.templ b/weed/admin/view/app/object_store_users.templ index b3b7d92de..e80b9e7e7 100644 --- a/weed/admin/view/app/object_store_users.templ +++ b/weed/admin/view/app/object_store_users.templ @@ -802,7 +802,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) { // Delete user function async function deleteUser(username) { - if (confirm(`Are you sure you want to delete user "${username}"? This action cannot be undone.`)) { + showDeleteConfirm(username, async function() { try { const response = await fetch(`/api/users/${username}`, { method: 'DELETE' @@ -819,6 +819,9 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) { console.error('Error deleting user:', error); showErrorMessage('Failed to delete user: ' + error.message); } + }, 'Are you sure you want to delete this user? This action cannot be undone.'); + } + } } } @@ -1102,7 +1105,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) { // Delete access key async function deleteAccessKey(username, accessKey) { - if (confirm('Are you sure you want to delete this access key?')) { + showDeleteConfirm(accessKey, async function() { try { const response = await fetch(`/api/users/${username}/access-keys/${accessKey}`, { method: 'DELETE' @@ -1121,6 +1124,9 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) { console.error('Error deleting access key:', error); showErrorMessage('Failed to delete access key: ' + error.message); } + }, 'Are you sure you want to delete this access key?'); + } + } } } diff --git a/weed/admin/view/app/object_store_users_templ.go b/weed/admin/view/app/object_store_users_templ.go index f03337207..08cc858d1 100644 --- a/weed/admin/view/app/object_store_users_templ.go +++ b/weed/admin/view/app/object_store_users_templ.go @@ -193,7 +193,7 @@ func ObjectStoreUsers(data dash.ObjectStoreUsersData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
Create New User
Hold Ctrl/Cmd to select multiple permissions
Apply selected permissions to specific buckets or all buckets
Hold Ctrl/Cmd to select multiple buckets
Hold Ctrl/Cmd to select multiple policies
Edit User
Apply selected permissions to specific buckets or all buckets
Hold Ctrl/Cmd to select multiple buckets
User Details
Manage Access Keys
Access Keys for
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
Create New User
Hold Ctrl/Cmd to select multiple permissions
Apply selected permissions to specific buckets or all buckets
Hold Ctrl/Cmd to select multiple buckets
Hold Ctrl/Cmd to select multiple policies
Edit User
Apply selected permissions to specific buckets or all buckets
Hold Ctrl/Cmd to select multiple buckets
User Details
Manage Access Keys
Access Keys for
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/policies.templ b/weed/admin/view/app/policies.templ index e613d535e..a61ac6b55 100644 --- a/weed/admin/view/app/policies.templ +++ b/weed/admin/view/app/policies.templ @@ -635,7 +635,7 @@ templ Policies(data dash.PoliciesData) { } function deletePolicy(policyName) { - if (confirm('Are you sure you want to delete policy "' + policyName + '"?')) { + showDeleteConfirm(policyName, function() { fetch('/api/object-store/policies/' + encodeURIComponent(policyName), { method: 'DELETE' }) @@ -643,7 +643,7 @@ templ Policies(data dash.PoliciesData) { .then(data => { if (data.success) { alert('Policy deleted successfully!'); - location.reload(); // Refresh the page + location.reload(); } else { alert('Error deleting policy: ' + (data.error || 'Unknown error')); } @@ -652,7 +652,7 @@ templ Policies(data dash.PoliciesData) { console.error('Error:', error); alert('Error deleting policy: ' + error.message); }); - } + }, 'Are you sure you want to delete this policy?'); } } \ No newline at end of file diff --git a/weed/admin/view/app/policies_templ.go b/weed/admin/view/app/policies_templ.go index 89aa83db5..30f92e158 100644 --- a/weed/admin/view/app/policies_templ.go +++ b/weed/admin/view/app/policies_templ.go @@ -193,7 +193,7 @@ func Policies(data dash.PoliciesData) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
Create IAM Policy
Enter a unique name for this policy (alphanumeric and underscores only)
Enter the policy document in AWS IAM JSON format
View IAM Policy
Loading...

Loading policy...

Edit IAM Policy
Policy name cannot be changed
Edit the policy document in AWS IAM JSON format
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
Create IAM Policy
Enter a unique name for this policy (alphanumeric and underscores only)
Enter the policy document in AWS IAM JSON format
View IAM Policy
Loading...

Loading policy...

Edit IAM Policy
Policy name cannot be changed
Edit the policy document in AWS IAM JSON format
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/service_accounts.templ b/weed/admin/view/app/service_accounts.templ index 5c85ac3ce..f0bb12b4c 100644 --- a/weed/admin/view/app/service_accounts.templ +++ b/weed/admin/view/app/service_accounts.templ @@ -373,7 +373,7 @@ templ ServiceAccounts(data dash.ServiceAccountsData) { } async function deleteSA(id) { - if (confirm('Are you sure you want to delete this service account? This action cannot be undone.')) { + showDeleteConfirm(id, async function() { try { const response = await fetch(`/api/service-accounts/${id}`, { method: 'DELETE' @@ -390,7 +390,7 @@ templ ServiceAccounts(data dash.ServiceAccountsData) { console.error('Error deleting service account:', error); showErrorMessage('Failed to delete: ' + error.message); } - } + }, 'Are you sure you want to delete this service account? This action cannot be undone.'); } async function handleCreateServiceAccount() { diff --git a/weed/admin/view/app/service_accounts_templ.go b/weed/admin/view/app/service_accounts_templ.go index d4fa05b5e..960152edf 100644 --- a/weed/admin/view/app/service_accounts_templ.go +++ b/weed/admin/view/app/service_accounts_templ.go @@ -272,7 +272,7 @@ func ServiceAccounts(data dash.ServiceAccountsData) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, " The service account will inherit permissions from this user
Leave empty for no expiration
Service Account Details
Service Account Created Successfully
Important: This is the only time you will see the secret access key. Please save it securely.
AWS CLI Configuration

Use these credentials to configure AWS CLI or SDKs:

Example AWS CLI Usage:
export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
export AWS_ENDPOINT_URL=http://localhost:8333
# List buckets
aws s3 ls
# Upload a file
aws s3 cp myfile.txt s3://mybucket/
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, " The service account will inherit permissions from this user
Leave empty for no expiration
Service Account Details
Service Account Created Successfully
Important: This is the only time you will see the secret access key. Please save it securely.
AWS CLI Configuration

Use these credentials to configure AWS CLI or SDKs:

Example AWS CLI Usage:
export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
export AWS_ENDPOINT_URL=http://localhost:8333
# List buckets
aws s3 ls
# Upload a file
aws s3 cp myfile.txt s3://mybucket/
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/task_config.templ b/weed/admin/view/app/task_config.templ index 81e089de6..41034cd09 100644 --- a/weed/admin/view/app/task_config.templ +++ b/weed/admin/view/app/task_config.templ @@ -104,13 +104,13 @@ templ TaskConfig(data *maintenance.TaskConfigData) {
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/task_config_templ.templ b/weed/admin/view/app/task_config_templ.templ index 010f5782c..37d236868 100644 --- a/weed/admin/view/app/task_config_templ.templ +++ b/weed/admin/view/app/task_config_templ.templ @@ -121,9 +121,9 @@ templ TaskConfigTempl(data *TaskConfigTemplData) { // Reset form function function resetForm() { - if (confirm('Are you sure you want to reset all changes?')) { + showConfirm('Are you sure you want to reset all changes?', function() { location.reload(); - } + }); } // Test configuration function diff --git a/weed/admin/view/app/task_config_templ_templ.go b/weed/admin/view/app/task_config_templ_templ.go index e037eb1cf..5b5d261b0 100644 --- a/weed/admin/view/app/task_config_templ_templ.go +++ b/weed/admin/view/app/task_config_templ_templ.go @@ -101,7 +101,7 @@ func TaskConfigTempl(data *TaskConfigTemplData) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/app/task_detail.templ b/weed/admin/view/app/task_detail.templ index 6045a5301..784e6dddf 100644 --- a/weed/admin/view/app/task_detail.templ +++ b/weed/admin/view/app/task_detail.templ @@ -1056,7 +1056,7 @@ templ TaskDetail(data *maintenance.TaskDetailData) { } function cancelTask(taskId) { - if (confirm('Are you sure you want to cancel this task?')) { + showConfirm('Are you sure you want to cancel this task?', function() { fetch(`/api/maintenance/tasks/${taskId}/cancel`, { method: 'POST', headers: { @@ -1076,7 +1076,7 @@ templ TaskDetail(data *maintenance.TaskDetailData) { console.error('Error:', error); alert('Error cancelling task'); }); - } + }); } function refreshTaskLogs(taskId) { diff --git a/weed/admin/view/app/task_detail_templ.go b/weed/admin/view/app/task_detail_templ.go index eec5ba29c..9d403c331 100644 --- a/weed/admin/view/app/task_detail_templ.go +++ b/weed/admin/view/app/task_detail_templ.go @@ -1617,7 +1617,7 @@ func TaskDetail(data *maintenance.TaskDetailData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 201, "\" onclick=\"exportTaskDetail(this.getAttribute('data-task-id'))\"> Export Details
Task Logs
Loading logs...

Fetching logs from worker...

Task: | Worker: | Entries:
Log Entries (Last 100) Newest entries first
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 201, "\" onclick=\"exportTaskDetail(this.getAttribute('data-task-id'))\"> Export Details
Task Logs
Loading logs...

Fetching logs from worker...

Task: | Worker: | Entries:
Log Entries (Last 100) Newest entries first
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/admin/view/layout/layout.templ b/weed/admin/view/layout/layout.templ index de74892f2..859984ca7 100644 --- a/weed/admin/view/layout/layout.templ +++ b/weed/admin/view/layout/layout.templ @@ -357,6 +357,8 @@ templ Layout(c *gin.Context, content templ.Component) { + + diff --git a/weed/admin/view/layout/layout_templ.go b/weed/admin/view/layout/layout_templ.go index f718127f5..7724b995e 100644 --- a/weed/admin/view/layout/layout_templ.go +++ b/weed/admin/view/layout/layout_templ.go @@ -517,7 +517,7 @@ func Layout(c *gin.Context, content templ.Component) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -553,7 +553,7 @@ func LoginForm(c *gin.Context, title string, errorMessage string) templ.Componen var templ_7745c5c3_Var28 string templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/layout/layout.templ`, Line: 371, Col: 17} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/layout/layout.templ`, Line: 373, Col: 17} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) if templ_7745c5c3_Err != nil { @@ -566,7 +566,7 @@ func LoginForm(c *gin.Context, title string, errorMessage string) templ.Componen var templ_7745c5c3_Var29 string templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/layout/layout.templ`, Line: 385, Col: 57} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/layout/layout.templ`, Line: 387, Col: 57} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) if templ_7745c5c3_Err != nil { @@ -584,7 +584,7 @@ func LoginForm(c *gin.Context, title string, errorMessage string) templ.Componen var templ_7745c5c3_Var30 string templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(errorMessage) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/layout/layout.templ`, Line: 392, Col: 45} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/layout/layout.templ`, Line: 394, Col: 45} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) if templ_7745c5c3_Err != nil { From 7e3bb4016e485a1f5f96e0c6f04b373ed47f1285 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sun, 25 Jan 2026 23:18:40 -0800 Subject: [PATCH 2/2] Fix syntax error in object_store_users.templ - remove duplicate closing braces --- weed/admin/view/app/object_store_users.templ | 6 ------ weed/admin/view/app/object_store_users_templ.go | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/weed/admin/view/app/object_store_users.templ b/weed/admin/view/app/object_store_users.templ index e80b9e7e7..3e8766196 100644 --- a/weed/admin/view/app/object_store_users.templ +++ b/weed/admin/view/app/object_store_users.templ @@ -821,9 +821,6 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) { } }, 'Are you sure you want to delete this user? This action cannot be undone.'); } - } - } - } // Handle create user form submission async function handleCreateUser() { @@ -1126,9 +1123,6 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) { } }, 'Are you sure you want to delete this access key?'); } - } - } - } // Utility functions diff --git a/weed/admin/view/app/object_store_users_templ.go b/weed/admin/view/app/object_store_users_templ.go index 08cc858d1..06b774814 100644 --- a/weed/admin/view/app/object_store_users_templ.go +++ b/weed/admin/view/app/object_store_users_templ.go @@ -193,7 +193,7 @@ func ObjectStoreUsers(data dash.ObjectStoreUsersData) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
Create New User
Hold Ctrl/Cmd to select multiple permissions
Apply selected permissions to specific buckets or all buckets
Hold Ctrl/Cmd to select multiple buckets
Hold Ctrl/Cmd to select multiple policies
Edit User
Apply selected permissions to specific buckets or all buckets
Hold Ctrl/Cmd to select multiple buckets
User Details
Manage Access Keys
Access Keys for
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
Create New User
Hold Ctrl/Cmd to select multiple permissions
Apply selected permissions to specific buckets or all buckets
Hold Ctrl/Cmd to select multiple buckets
Hold Ctrl/Cmd to select multiple policies
Edit User
Apply selected permissions to specific buckets or all buckets
Hold Ctrl/Cmd to select multiple buckets
User Details
Manage Access Keys
Access Keys for
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }