Admin UI add maintenance menu (#6944)

* add ui for maintenance

* valid config loading. fix workers page.

* refactor

* grpc between admin and workers

* add a long-running bidirectional grpc call between admin and worker
* use the grpc call to heartbeat
* use the grpc call to communicate
* worker can remove the http client
* admin uses http port + 10000 as its default grpc port

* one task one package

* handles connection failures gracefully with exponential backoff

* grpc with insecure tls

* grpc with optional tls

* fix detecting tls

* change time config from nano seconds to seconds

* add tasks with 3 interfaces

* compiles reducing hard coded

* remove a couple of tasks

* remove hard coded references

* reduce hard coded values

* remove hard coded values

* remove hard coded from templ

* refactor maintenance package

* fix import cycle

* simplify

* simplify

* auto register

* auto register factory

* auto register task types

* self register types

* refactor

* simplify

* remove one task

* register ui

* lazy init executor factories

* use registered task types

* DefaultWorkerConfig remove hard coded task types

* remove more hard coded

* implement get maintenance task

* dynamic task configuration

* "System Settings" should only have system level settings

* adjust menu for tasks

* ensure menu not collapsed

* render job configuration well

* use templ for ui of task configuration

* fix ordering

* fix bugs

* saving duration in seconds

* use value and unit for duration

* Delete WORKER_REFACTORING_PLAN.md

* Delete maintenance.json

* Delete custom_worker_example.go

* remove address from workers

* remove old code from ec task

* remove creating collection button

* reconnect with exponential backoff

* worker use security.toml

* start admin server with tls info from security.toml

* fix "weed admin" cli description
This commit is contained in:
Chris Lu
2025-07-06 13:57:02 -07:00
committed by GitHub
parent 302e62d480
commit aa66852304
76 changed files with 18218 additions and 206 deletions

View File

@@ -129,6 +129,21 @@ function setupSubmenuBehavior() {
}
}
// If we're on a maintenance page, expand the maintenance submenu
if (currentPath.startsWith('/maintenance')) {
const maintenanceSubmenu = document.getElementById('maintenanceSubmenu');
if (maintenanceSubmenu) {
maintenanceSubmenu.classList.add('show');
// Update the parent toggle button state
const toggleButton = document.querySelector('[data-bs-target="#maintenanceSubmenu"]');
if (toggleButton) {
toggleButton.classList.remove('collapsed');
toggleButton.setAttribute('aria-expanded', 'true');
}
}
}
// Prevent submenu from collapsing when clicking on submenu items
const clusterSubmenuLinks = document.querySelectorAll('#clusterSubmenu .nav-link');
clusterSubmenuLinks.forEach(function(link) {
@@ -146,6 +161,14 @@ function setupSubmenuBehavior() {
});
});
const maintenanceSubmenuLinks = document.querySelectorAll('#maintenanceSubmenu .nav-link');
maintenanceSubmenuLinks.forEach(function(link) {
link.addEventListener('click', function(e) {
// Don't prevent the navigation, just stop the collapse behavior
e.stopPropagation();
});
});
// Handle the main cluster toggle
const clusterToggle = document.querySelector('[data-bs-target="#clusterSubmenu"]');
if (clusterToggle) {
@@ -191,6 +214,29 @@ function setupSubmenuBehavior() {
}
});
}
// Handle the main maintenance toggle
const maintenanceToggle = document.querySelector('[data-bs-target="#maintenanceSubmenu"]');
if (maintenanceToggle) {
maintenanceToggle.addEventListener('click', function(e) {
e.preventDefault();
const submenu = document.getElementById('maintenanceSubmenu');
const isExpanded = submenu.classList.contains('show');
if (isExpanded) {
// Collapse
submenu.classList.remove('show');
this.classList.add('collapsed');
this.setAttribute('aria-expanded', 'false');
} else {
// Expand
submenu.classList.add('show');
this.classList.remove('collapsed');
this.setAttribute('aria-expanded', 'true');
}
});
}
}
// Loading indicator functions
@@ -689,7 +735,7 @@ function exportVolumes() {
for (let i = 0; i < cells.length - 1; i++) {
rowData.push(`"${cells[i].textContent.trim().replace(/"/g, '""')}"`);
}
csv += rowData.join(',') + '\n';
csv += rowData.join(',') + '\n';
});
downloadCSV(csv, 'seaweedfs-volumes.csv');
@@ -877,53 +923,7 @@ async function deleteCollection(collectionName) {
}
}
// Handle create collection form submission
document.addEventListener('DOMContentLoaded', function() {
const createCollectionForm = document.getElementById('createCollectionForm');
if (createCollectionForm) {
createCollectionForm.addEventListener('submit', handleCreateCollection);
}
});
async function handleCreateCollection(event) {
event.preventDefault();
const formData = new FormData(event.target);
const collectionData = {
name: formData.get('name'),
replication: formData.get('replication'),
diskType: formData.get('diskType')
};
try {
const response = await fetch('/api/collections', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(collectionData)
});
if (response.ok) {
showSuccessMessage(`Collection "${collectionData.name}" created successfully`);
// Hide modal
const modal = bootstrap.Modal.getInstance(document.getElementById('createCollectionModal'));
modal.hide();
// Reset form
event.target.reset();
// Refresh page
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
const error = await response.json();
showErrorMessage(`Failed to create collection: ${error.error || 'Unknown error'}`);
}
} catch (error) {
console.error('Error creating collection:', error);
showErrorMessage('Failed to create collection. Please try again.');
}
}
// Download CSV utility function
function downloadCSV(csvContent, filename) {