mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-07-30 20:13:23 +00:00
The plugin.templ and plugin_lane.templ components use basePath() in their IIFE (Immediately Invoked Function Expression) scopes to handle subdirectory deployments. However, basePath was not defined locally, causing "basePath is not defined" errors when accessing plugin pages. Added local basePath function definitions in both files, matching the pattern from admin.js. This function checks window.__BASE_PATH__ (set by the layout during page initialization) and prepends it to API paths.
172 lines
8.4 KiB
Templ
172 lines
8.4 KiB
Templ
package app
|
|
|
|
templ PluginLane(page string, lane string) {
|
|
{{
|
|
currentPage := page
|
|
if currentPage == "" {
|
|
currentPage = "lane_workers"
|
|
}
|
|
}}
|
|
<div class="container-fluid" id="plugin-lane-page" data-plugin-page={ currentPage } data-plugin-lane={ lane }>
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
|
|
<div>
|
|
<h2 class="mb-0"><i class="fas fa-layer-group me-2"></i>{ lane } Lane Workers</h2>
|
|
<p class="text-muted mb-0">Workers and scheduler status for the { lane } scheduling lane.</p>
|
|
</div>
|
|
<div class="btn-group">
|
|
<a href="/plugin" class="btn btn-outline-secondary">
|
|
<i class="fas fa-arrow-left me-1"></i>All Workers
|
|
</a>
|
|
<button type="button" class="btn btn-outline-secondary" id="plugin-lane-refresh-btn">
|
|
<i class="fas fa-sync-alt me-1"></i>Refresh
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0"><i class="fas fa-server me-2"></i>Lane Scheduler Status</h5>
|
|
</div>
|
|
<div class="card-body" id="plugin-lane-scheduler-status">
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fas fa-spinner fa-spin me-1"></i>Loading scheduler status...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0"><i class="fas fa-tasks me-2"></i>Job Types</h5>
|
|
</div>
|
|
<div class="card-body" id="plugin-lane-job-types">
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fas fa-spinner fa-spin me-1"></i>Loading job types...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0"><i class="fas fa-plug me-2"></i>Connected Workers</h5>
|
|
</div>
|
|
<div class="card-body" id="plugin-lane-workers">
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fas fa-spinner fa-spin me-1"></i>Loading workers...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
(function() {
|
|
const page = document.getElementById('plugin-lane-page');
|
|
if (!page) return;
|
|
const lane = page.dataset.pluginLane || '';
|
|
if (!lane) return;
|
|
|
|
function basePath(path) {
|
|
return (window.__BASE_PATH__ || '') + path;
|
|
}
|
|
|
|
function fetchAndRender(url, containerId, renderFn) {
|
|
fetch(basePath(url))
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
const el = document.getElementById(containerId);
|
|
if (el) el.innerHTML = renderFn(data);
|
|
})
|
|
.catch(err => {
|
|
const el = document.getElementById(containerId);
|
|
if (el) el.innerHTML = '<div class="alert alert-danger">Failed to load: ' + err.message + '</div>';
|
|
});
|
|
}
|
|
|
|
function renderSchedulerStatus(data) {
|
|
if (!data || !data.scheduler) return '<div class="text-muted">No scheduler data</div>';
|
|
const s = data.scheduler;
|
|
let html = '<div class="row">';
|
|
html += '<div class="col-md-3"><strong>Phase:</strong> ' + (s.current_phase || 'idle') + '</div>';
|
|
html += '<div class="col-md-3"><strong>Idle Sleep:</strong> ' + (s.idle_sleep_seconds || 0) + 's</div>';
|
|
html += '<div class="col-md-3"><strong>Current Job Type:</strong> ' + (s.current_job_type || '-') + '</div>';
|
|
html += '<div class="col-md-3"><strong>Next Detection:</strong> ' + (s.next_detection_at ? new Date(s.next_detection_at).toLocaleTimeString() : '-') + '</div>';
|
|
html += '</div>';
|
|
if (s.job_types && s.job_types.length > 0) {
|
|
html += '<hr><table class="table table-sm table-hover mb-0"><thead><tr><th>Job Type</th><th>Enabled</th><th>Interval</th><th>In Flight</th><th>Next Detection</th></tr></thead><tbody>';
|
|
s.job_types.forEach(jt => {
|
|
html += '<tr>';
|
|
html += '<td>' + jt.job_type + '</td>';
|
|
html += '<td>' + (jt.enabled ? '<span class="badge bg-success">Yes</span>' : '<span class="badge bg-secondary">No</span>') + '</td>';
|
|
html += '<td>' + (jt.detection_interval_seconds || '-') + 's</td>';
|
|
html += '<td>' + (jt.detection_in_flight ? '<span class="badge bg-warning">Yes</span>' : 'No') + '</td>';
|
|
html += '<td>' + (jt.next_detection_at ? new Date(jt.next_detection_at).toLocaleTimeString() : '-') + '</td>';
|
|
html += '</tr>';
|
|
});
|
|
html += '</tbody></table>';
|
|
}
|
|
return html;
|
|
}
|
|
|
|
function renderJobTypes(data) {
|
|
if (!data || data.length === 0) return '<div class="text-muted">No job types in this lane</div>';
|
|
const filtered = data.filter(s => s.lane === lane);
|
|
if (filtered.length === 0) return '<div class="text-muted">No job types in this lane</div>';
|
|
let html = '<table class="table table-sm table-hover mb-0"><thead><tr><th>Job Type</th><th>Enabled</th><th>Concurrency</th><th>Detection Interval</th><th>Status</th></tr></thead><tbody>';
|
|
filtered.forEach(s => {
|
|
html += '<tr>';
|
|
html += '<td><a href="' + basePath('/plugin/configuration?job=' + s.job_type) + '">' + s.job_type + '</a></td>';
|
|
html += '<td>' + (s.enabled ? '<span class="badge bg-success">Yes</span>' : '<span class="badge bg-secondary">No</span>') + '</td>';
|
|
html += '<td>' + (s.global_execution_concurrency || 1) + '</td>';
|
|
html += '<td>' + (s.detection_interval_seconds || '-') + 's</td>';
|
|
html += '<td>' + (s.last_run_status || '-') + '</td>';
|
|
html += '</tr>';
|
|
});
|
|
html += '</tbody></table>';
|
|
return html;
|
|
}
|
|
|
|
function renderWorkers(data) {
|
|
if (!data || data.length === 0) return '<div class="text-muted">No workers connected for this lane</div>';
|
|
let html = '<table class="table table-sm table-hover mb-0"><thead><tr><th>Worker ID</th><th>Address</th><th>Job Types</th><th>Connected</th><th>Last Seen</th></tr></thead><tbody>';
|
|
data.forEach(w => {
|
|
const jobTypes = Object.keys(w.Capabilities || {}).join(', ');
|
|
html += '<tr>';
|
|
html += '<td>' + (w.WorkerID || '-') + '</td>';
|
|
html += '<td>' + (w.Address || '-') + '</td>';
|
|
html += '<td>' + jobTypes + '</td>';
|
|
html += '<td>' + (w.ConnectedAt ? new Date(w.ConnectedAt).toLocaleString() : '-') + '</td>';
|
|
html += '<td>' + (w.LastSeenAt ? new Date(w.LastSeenAt).toLocaleString() : '-') + '</td>';
|
|
html += '</tr>';
|
|
});
|
|
html += '</tbody></table>';
|
|
return html;
|
|
}
|
|
|
|
function refresh() {
|
|
fetchAndRender('/api/plugin/scheduler-status?lane=' + lane, 'plugin-lane-scheduler-status', renderSchedulerStatus);
|
|
fetchAndRender('/api/plugin/scheduler-states?lane=' + lane, 'plugin-lane-job-types', renderJobTypes);
|
|
fetchAndRender('/api/plugin/workers?lane=' + lane, 'plugin-lane-workers', renderWorkers);
|
|
}
|
|
|
|
refresh();
|
|
const btn = document.getElementById('plugin-lane-refresh-btn');
|
|
if (btn) btn.addEventListener('click', refresh);
|
|
})();
|
|
</script>
|
|
}
|