1
0
Fork 0
mirror of https://github.com/chrislusf/seaweedfs synced 2025-07-26 21:42:48 +02:00
seaweedfs/weed/admin/view/app/task_config_templ.templ
Chris Lu aa66852304
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
2025-07-06 13:57:02 -07:00

160 lines
No EOL
6.1 KiB
Text

package app
import (
"github.com/seaweedfs/seaweedfs/weed/admin/maintenance"
"github.com/seaweedfs/seaweedfs/weed/admin/view/components"
)
// TaskConfigTemplData represents data for templ-based task configuration
type TaskConfigTemplData struct {
TaskType maintenance.MaintenanceTaskType
TaskName string
TaskIcon string
Description string
ConfigSections []components.ConfigSectionData
}
templ TaskConfigTempl(data *TaskConfigTemplData) {
<div class="container-fluid">
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<h2 class="mb-0">
<i class={data.TaskIcon + " me-2"}></i>
{data.TaskName} Configuration
</h2>
<div class="btn-group">
<a href="/maintenance/config" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-1"></i>
Back to Configuration
</a>
<a href="/maintenance/queue" class="btn btn-outline-info">
<i class="fas fa-list me-1"></i>
View Queue
</a>
</div>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-12">
<div class="alert alert-info" role="alert">
<i class="fas fa-info-circle me-2"></i>
{data.Description}
</div>
</div>
</div>
<form method="POST" class="needs-validation" novalidate>
<!-- Render all configuration sections -->
for _, section := range data.ConfigSections {
@components.ConfigSection(section)
}
<!-- Form actions -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>
Save Configuration
</button>
<button type="button" class="btn btn-outline-secondary ms-2" onclick="resetForm()">
<i class="fas fa-undo me-1"></i>
Reset
</button>
</div>
<div>
<button type="button" class="btn btn-outline-info" onclick="testConfiguration()">
<i class="fas fa-play me-1"></i>
Test Configuration
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<script>
// Form validation
(function() {
'use strict';
window.addEventListener('load', function() {
var forms = document.getElementsByClassName('needs-validation');
var validation = Array.prototype.filter.call(forms, function(form) {
form.addEventListener('submit', function(event) {
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated');
}, false);
});
}, false);
})();
// Auto-save functionality
let autoSaveTimeout;
function autoSave() {
clearTimeout(autoSaveTimeout);
autoSaveTimeout = setTimeout(function() {
const formData = new FormData(document.querySelector('form'));
localStorage.setItem('task_config_' + '{data.TaskType}', JSON.stringify(Object.fromEntries(formData)));
}, 1000);
}
// Add auto-save listeners to all form inputs
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('form');
if (form) {
form.addEventListener('input', autoSave);
form.addEventListener('change', autoSave);
}
});
// Reset form function
function resetForm() {
if (confirm('Are you sure you want to reset all changes?')) {
location.reload();
}
}
// Test configuration function
function testConfiguration() {
const formData = new FormData(document.querySelector('form'));
// Show loading state
const testBtn = document.querySelector('button[onclick="testConfiguration()"]');
const originalContent = testBtn.innerHTML;
testBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Testing...';
testBtn.disabled = true;
fetch('/maintenance/config/{data.TaskType}/test', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Configuration test successful!');
} else {
alert('Configuration test failed: ' + data.error);
}
})
.catch(error => {
alert('Test failed: ' + error);
})
.finally(() => {
testBtn.innerHTML = originalContent;
testBtn.disabled = false;
});
}
</script>
}