fix(admin/view): define basePath in plugin IIFE scopes (#9298)

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.
This commit is contained in:
Chris Lu authored and GitHub committed 2026-05-01 18:22:39 -07:00
1 parent 913f98db10
commit f2c3bd7b77
4 files changed
+10 -2

No files matched your search

+4
View File
@@ -597,6 +597,10 @@ templ Plugin(page string, initialJob string, lane string) {
var pluginLane = String(page.getAttribute('data-plugin-lane') || '').trim();
function basePath(path) {
return (window.__BASE_PATH__ || '') + path;
}
var state = {
selectedJobType: '',
jobTypes: [],
+4
View File
@@ -80,6 +80,10 @@ templ PluginLane(page string, lane string) {
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())
+1 -1
View File
@@ -85,7 +85,7 @@ func PluginLane(page string, lane string) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " 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>\n (function() {\n const page = document.getElementById('plugin-lane-page');\n if (!page) return;\n const lane = page.dataset.pluginLane || '';\n if (!lane) return;\n\n function fetchAndRender(url, containerId, renderFn) {\n fetch(basePath(url))\n .then(r => r.json())\n .then(data => {\n const el = document.getElementById(containerId);\n if (el) el.innerHTML = renderFn(data);\n })\n .catch(err => {\n const el = document.getElementById(containerId);\n if (el) el.innerHTML = '<div class=\"alert alert-danger\">Failed to load: ' + err.message + '</div>';\n });\n }\n\n function renderSchedulerStatus(data) {\n if (!data || !data.scheduler) return '<div class=\"text-muted\">No scheduler data</div>';\n const s = data.scheduler;\n let html = '<div class=\"row\">';\n html += '<div class=\"col-md-3\"><strong>Phase:</strong> ' + (s.current_phase || 'idle') + '</div>';\n html += '<div class=\"col-md-3\"><strong>Idle Sleep:</strong> ' + (s.idle_sleep_seconds || 0) + 's</div>';\n html += '<div class=\"col-md-3\"><strong>Current Job Type:</strong> ' + (s.current_job_type || '-') + '</div>';\n html += '<div class=\"col-md-3\"><strong>Next Detection:</strong> ' + (s.next_detection_at ? new Date(s.next_detection_at).toLocaleTimeString() : '-') + '</div>';\n html += '</div>';\n if (s.job_types && s.job_types.length > 0) {\n 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>';\n s.job_types.forEach(jt => {\n html += '<tr>';\n html += '<td>' + jt.job_type + '</td>';\n html += '<td>' + (jt.enabled ? '<span class=\"badge bg-success\">Yes</span>' : '<span class=\"badge bg-secondary\">No</span>') + '</td>';\n html += '<td>' + (jt.detection_interval_seconds || '-') + 's</td>';\n html += '<td>' + (jt.detection_in_flight ? '<span class=\"badge bg-warning\">Yes</span>' : 'No') + '</td>';\n html += '<td>' + (jt.next_detection_at ? new Date(jt.next_detection_at).toLocaleTimeString() : '-') + '</td>';\n html += '</tr>';\n });\n html += '</tbody></table>';\n }\n return html;\n }\n\n function renderJobTypes(data) {\n if (!data || data.length === 0) return '<div class=\"text-muted\">No job types in this lane</div>';\n const filtered = data.filter(s => s.lane === lane);\n if (filtered.length === 0) return '<div class=\"text-muted\">No job types in this lane</div>';\n 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>';\n filtered.forEach(s => {\n html += '<tr>';\n html += '<td><a href=\"' + basePath('/plugin/configuration?job=' + s.job_type) + '\">' + s.job_type + '</a></td>';\n html += '<td>' + (s.enabled ? '<span classLine truncated
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " 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>\n (function() {\n const page = document.getElementById('plugin-lane-page');\n if (!page) return;\n const lane = page.dataset.pluginLane || '';\n if (!lane) return;\n\n function basePath(path) {\n return (window.__BASE_PATH__ || '') + path;\n }\n\n function fetchAndRender(url, containerId, renderFn) {\n fetch(basePath(url))\n .then(r => r.json())\n .then(data => {\n const el = document.getElementById(containerId);\n if (el) el.innerHTML = renderFn(data);\n })\n .catch(err => {\n const el = document.getElementById(containerId);\n if (el) el.innerHTML = '<div class=\"alert alert-danger\">Failed to load: ' + err.message + '</div>';\n });\n }\n\n function renderSchedulerStatus(data) {\n if (!data || !data.scheduler) return '<div class=\"text-muted\">No scheduler data</div>';\n const s = data.scheduler;\n let html = '<div class=\"row\">';\n html += '<div class=\"col-md-3\"><strong>Phase:</strong> ' + (s.current_phase || 'idle') + '</div>';\n html += '<div class=\"col-md-3\"><strong>Idle Sleep:</strong> ' + (s.idle_sleep_seconds || 0) + 's</div>';\n html += '<div class=\"col-md-3\"><strong>Current Job Type:</strong> ' + (s.current_job_type || '-') + '</div>';\n html += '<div class=\"col-md-3\"><strong>Next Detection:</strong> ' + (s.next_detection_at ? new Date(s.next_detection_at).toLocaleTimeString() : '-') + '</div>';\n html += '</div>';\n if (s.job_types && s.job_types.length > 0) {\n 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>';\n s.job_types.forEach(jt => {\n html += '<tr>';\n html += '<td>' + jt.job_type + '</td>';\n html += '<td>' + (jt.enabled ? '<span class=\"badge bg-success\">Yes</span>' : '<span class=\"badge bg-secondary\">No</span>') + '</td>';\n html += '<td>' + (jt.detection_interval_seconds || '-') + 's</td>';\n html += '<td>' + (jt.detection_in_flight ? '<span class=\"badge bg-warning\">Yes</span>' : 'No') + '</td>';\n html += '<td>' + (jt.next_detection_at ? new Date(jt.next_detection_at).toLocaleTimeString() : '-') + '</td>';\n html += '</tr>';\n });\n html += '</tbody></table>';\n }\n return html;\n }\n\n function renderJobTypes(data) {\n if (!data || data.length === 0) return '<div class=\"text-muted\">No job types in this lane</div>';\n const filtered = data.filter(s => s.lane === lane);\n if (filtered.length === 0) return '<div class=\"text-muted\">No job types in this lane</div>';\n 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>';\n filtered.forEach(s => {\n html += '<tr>';\n html += '<td><a href=\"' + basePath('/plugin/configuration?job=' + s.Line truncated
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
+1 -1
View File
@@ -104,7 +104,7 @@ func Plugin(page string, initialJob string, lane string) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</p></div><div class=\"btn-group\"><button type=\"button\" class=\"btn btn-outline-secondary\" id=\"plugin-refresh-all-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\"><ul class=\"nav nav-tabs\" id=\"plugin-top-tabs\"><li class=\"nav-item\"><button type=\"button\" class=\"nav-link active\" data-plugin-top-tab=\"overview\"><i class=\"fas fa-chart-line me-1\"></i>Overview</button></li></ul></div></div><div class=\"row mb-3 d-none\" id=\"plugin-subtab-row\"><div class=\"col-12\"><ul class=\"nav nav-pills gap-2\" id=\"plugin-subtabs\"><li class=\"nav-item\"><button type=\"button\" class=\"nav-link active\" data-plugin-subtab=\"configuration\"><i class=\"fas fa-sliders-h me-1\"></i>Configuration</button></li><li class=\"nav-item\"><button type=\"button\" class=\"nav-link\" data-plugin-subtab=\"detection\"><i class=\"fas fa-search me-1\"></i>Job Detection</button></li><li class=\"nav-item\"><button type=\"button\" class=\"nav-link\" data-plugin-subtab=\"queue\"><i class=\"fas fa-list me-1\"></i>Job Queue</button></li><li class=\"nav-item\"><button type=\"button\" class=\"nav-link\" data-plugin-subtab=\"execution\"><i class=\"fas fa-tasks me-1\"></i>Job Execution</button></li></ul></div></div><div class=\"plugin-section plugin-section-overview\"><div class=\"row mb-4\"><div class=\"col-md-3 mb-3\"><div class=\"card border-info h-100\"><div class=\"card-body text-center\"><div class=\"text-uppercase text-muted small mb-1\">Workers</div><h4 class=\"mb-0\" id=\"plugin-status-workers\">0</h4></div></div></div><div class=\"col-md-3 mb-3\"><div class=\"card border-warning h-100\"><div class=\"card-body text-center\"><div class=\"text-uppercase text-muted small mb-1\">Active Jobs</div><h4 class=\"mb-0\" id=\"plugin-status-active-jobs\">0</h4></div></div></div><div class=\"col-md-3 mb-3\"><div class=\"card border-success h-100\"><div class=\"card-body text-center\"><div class=\"text-uppercase text-muted small mb-1\">Activities (recent)</div><h4 class=\"mb-0\" id=\"plugin-status-activities\">0</h4></div></div></div><div class=\"col-md-3 mb-3\"><div class=\"card border-secondary h-100\"><div class=\"card-body text-center\"><div class=\"text-uppercase text-muted small mb-1\">Next Run</div><h4 class=\"mb-0 plugin-scheduler-next-run\">-</h4><div class=\"text-muted small plugin-scheduler-next-run-meta\"></div></div></div></div></div><div class=\"row mb-4\"><div class=\"col-12\"><div class=\"card shadow-sm\"><div class=\"card-header\"><h5 class=\"mb-0\"><i class=\"fas fa-chart-bar me-2\"></i>Per Job Type Summary</h5></div><div class=\"card-body p-0\"><div class=\"table-responsive\"><table class=\"table table-sm table-hover mb-0\"><thead class=\"table-light\"><tr><th>Job Type</th><th>Active Jobs</th><th>Recent Activities</th></tr></thead> <tbody id=\"plugin-jobtype-summary-body\"><tr><td colspan=\"3\" class=\"text-muted text-center py-3\">Loading...</td></tr></tbody></table></div></div></div></div></div><div class=\"row mb-4\"><div class=\"col-12\"><div class=\"card shadow-sm\"><div class=\"card-header d-flex justify-content-between align-items-center flex-wrap gap-2\"><h5 class=\"mb-0\"><i class=\"fas fa-clock me-2\"></i>Scheduler State</h5><small class=\"text-muted\">Sequential scheduler with per-job runtime limits</small></div><div class=\"card-body p-0\"><div class=\"table-responsive\"><table class=\"table table-sm table-hover mb-0\"><thead class=\"table-light\"><tr><th>Job Type</th><th>Enabled</th><th>Detector</th><th>In Flight</th><th>Max Runtime</th><th>Exec Global</th><th>Exec/Worker</th><th>Executor Workers</th><th>Effective Exec</th><th>Last Run</th></tr></thead> <tbody id=\"plugin-scheduler-table-body\"><tr><td colspan=\"10\" class=\"text-muted text-center py-3\">Loading...</td></tr></tbody></table></div></div></div></div></div><div class=\"row mb-4\"><div class=\"col-12\"><div class=\"card shadow-sm\"><div class=\"card-header\"><h5 class=\"mb-0\"><i class=\"fas fa-users me-2\"></i>Workers</h5></div><div class=\"card-body p-0\"><div class=\"table-responsive\" style=\"max-height: 560px; overflow-y: auto;\"><table class=\"table table-sm table-hover mb-0\"><thead class=\"table-light sticky-top\"><tr><th>Worker</th><th>Address</th><th>Capabilities</th><th>Load</th></tr></thead> <tbody id=\"plugin-workers-table-body\"><tr><td colspan=\"4\" class=\"text-muted text-center py-3\">Loading...</td></tr></tbody></table></div></div></div></div></div></div><div class=\"plugin-section plugin-section-configuration\"><div class=\"row mb-4\"><div class=\"col-lg-8 mb-3\"><div class=\"card shadow-sm h-100\"><div class=\"card-header d-flex justify-content-between align-items-center\"><h5 class=\"mb-0\"><i class=\"fas fa-sliders-h me-2\"></i>Job Type Configuration</h5><small class=\"text-muted\" id=\"plugin-config-updated-at\">Not loaded</small></div><div class=\"card-body\"><div class=Line truncated
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</p></div><div class=\"btn-group\"><button type=\"button\" class=\"btn btn-outline-secondary\" id=\"plugin-refresh-all-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\"><ul class=\"nav nav-tabs\" id=\"plugin-top-tabs\"><li class=\"nav-item\"><button type=\"button\" class=\"nav-link active\" data-plugin-top-tab=\"overview\"><i class=\"fas fa-chart-line me-1\"></i>Overview</button></li></ul></div></div><div class=\"row mb-3 d-none\" id=\"plugin-subtab-row\"><div class=\"col-12\"><ul class=\"nav nav-pills gap-2\" id=\"plugin-subtabs\"><li class=\"nav-item\"><button type=\"button\" class=\"nav-link active\" data-plugin-subtab=\"configuration\"><i class=\"fas fa-sliders-h me-1\"></i>Configuration</button></li><li class=\"nav-item\"><button type=\"button\" class=\"nav-link\" data-plugin-subtab=\"detection\"><i class=\"fas fa-search me-1\"></i>Job Detection</button></li><li class=\"nav-item\"><button type=\"button\" class=\"nav-link\" data-plugin-subtab=\"queue\"><i class=\"fas fa-list me-1\"></i>Job Queue</button></li><li class=\"nav-item\"><button type=\"button\" class=\"nav-link\" data-plugin-subtab=\"execution\"><i class=\"fas fa-tasks me-1\"></i>Job Execution</button></li></ul></div></div><div class=\"plugin-section plugin-section-overview\"><div class=\"row mb-4\"><div class=\"col-md-3 mb-3\"><div class=\"card border-info h-100\"><div class=\"card-body text-center\"><div class=\"text-uppercase text-muted small mb-1\">Workers</div><h4 class=\"mb-0\" id=\"plugin-status-workers\">0</h4></div></div></div><div class=\"col-md-3 mb-3\"><div class=\"card border-warning h-100\"><div class=\"card-body text-center\"><div class=\"text-uppercase text-muted small mb-1\">Active Jobs</div><h4 class=\"mb-0\" id=\"plugin-status-active-jobs\">0</h4></div></div></div><div class=\"col-md-3 mb-3\"><div class=\"card border-success h-100\"><div class=\"card-body text-center\"><div class=\"text-uppercase text-muted small mb-1\">Activities (recent)</div><h4 class=\"mb-0\" id=\"plugin-status-activities\">0</h4></div></div></div><div class=\"col-md-3 mb-3\"><div class=\"card border-secondary h-100\"><div class=\"card-body text-center\"><div class=\"text-uppercase text-muted small mb-1\">Next Run</div><h4 class=\"mb-0 plugin-scheduler-next-run\">-</h4><div class=\"text-muted small plugin-scheduler-next-run-meta\"></div></div></div></div></div><div class=\"row mb-4\"><div class=\"col-12\"><div class=\"card shadow-sm\"><div class=\"card-header\"><h5 class=\"mb-0\"><i class=\"fas fa-chart-bar me-2\"></i>Per Job Type Summary</h5></div><div class=\"card-body p-0\"><div class=\"table-responsive\"><table class=\"table table-sm table-hover mb-0\"><thead class=\"table-light\"><tr><th>Job Type</th><th>Active Jobs</th><th>Recent Activities</th></tr></thead> <tbody id=\"plugin-jobtype-summary-body\"><tr><td colspan=\"3\" class=\"text-muted text-center py-3\">Loading...</td></tr></tbody></table></div></div></div></div></div><div class=\"row mb-4\"><div class=\"col-12\"><div class=\"card shadow-sm\"><div class=\"card-header d-flex justify-content-between align-items-center flex-wrap gap-2\"><h5 class=\"mb-0\"><i class=\"fas fa-clock me-2\"></i>Scheduler State</h5><small class=\"text-muted\">Sequential scheduler with per-job runtime limits</small></div><div class=\"card-body p-0\"><div class=\"table-responsive\"><table class=\"table table-sm table-hover mb-0\"><thead class=\"table-light\"><tr><th>Job Type</th><th>Enabled</th><th>Detector</th><th>In Flight</th><th>Max Runtime</th><th>Exec Global</th><th>Exec/Worker</th><th>Executor Workers</th><th>Effective Exec</th><th>Last Run</th></tr></thead> <tbody id=\"plugin-scheduler-table-body\"><tr><td colspan=\"10\" class=\"text-muted text-center py-3\">Loading...</td></tr></tbody></table></div></div></div></div></div><div class=\"row mb-4\"><div class=\"col-12\"><div class=\"card shadow-sm\"><div class=\"card-header\"><h5 class=\"mb-0\"><i class=\"fas fa-users me-2\"></i>Workers</h5></div><div class=\"card-body p-0\"><div class=\"table-responsive\" style=\"max-height: 560px; overflow-y: auto;\"><table class=\"table table-sm table-hover mb-0\"><thead class=\"table-light sticky-top\"><tr><th>Worker</th><th>Address</th><th>Capabilities</th><th>Load</th></tr></thead> <tbody id=\"plugin-workers-table-body\"><tr><td colspan=\"4\" class=\"text-muted text-center py-3\">Loading...</td></tr></tbody></table></div></div></div></div></div></div><div class=\"plugin-section plugin-section-configuration\"><div class=\"row mb-4\"><div class=\"col-lg-8 mb-3\"><div class=\"card shadow-sm h-100\"><div class=\"card-header d-flex justify-content-between align-items-center\"><h5 class=\"mb-0\"><i class=\"fas fa-sliders-h me-2\"></i>Job Type Configuration</h5><small class=\"text-muted\" id=\"plugin-config-updated-at\">Not loaded</small></div><div class=\"card-body\"><div class=Line truncated
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}