feat: pre-computed state store and async ACME issuance (fixes timeout mismatch)
- Add lib/state.py: in-memory state store with subsystem collectors (firewall, dnsmasq, nginx, acme, wireguard) - Refactor all handlers: read from state on GET, call refresh_state() after mutations instead of invoking subprocesses per request - daemon/server.py: add refresh_state(), /status/all, /status/refresh; populate state at startup - webui/api/certs.py: async step-by-step ACME issuance (validate, issue with request_id, poll status) replacing blocking endpoint - webui/server.py: render pages from state instead of direct lib calls - Update templates, JS for async cert issuance with polling UI - Update tests for state-based mocking; add test_state.py - Fix SIM105 lint issue (contextlib.suppress) - Add TODO.md with certificate issuance issue tracking Resolves: WebUI 30s timeout freeze during cert issuance (Problem 1)
This commit is contained in:
+27
-15
@@ -89,36 +89,34 @@
|
||||
var refreshInterval = {{ (refresh_interval | default(15)) }};
|
||||
var currentTab = 'journal';
|
||||
|
||||
function loadTabEl(el) {
|
||||
var url = el.getAttribute('hx-get');
|
||||
if (!url) return;
|
||||
el.textContent = 'Loading...';
|
||||
fetch(url).then(function(r) { return r.text(); })
|
||||
.then(function(html) { el.innerHTML = html; })
|
||||
.catch(function() { el.innerHTML = '<div class="log-line">(failed to load log)</div>'; });
|
||||
}
|
||||
|
||||
function setActivePolling() {
|
||||
document.querySelectorAll('.log-viewer').forEach(function(el) {
|
||||
htmx.abort(el);
|
||||
if (typeof htmx !== 'undefined') htmx.abort(el);
|
||||
el.setAttribute('hx-trigger', 'none');
|
||||
});
|
||||
var activeEl = document.getElementById('log-' + currentTab);
|
||||
if (activeEl) {
|
||||
activeEl.setAttribute('hx-trigger', 'every ' + refreshInterval + 's');
|
||||
}
|
||||
if (typeof htmx !== 'undefined') htmx.process(document.body);
|
||||
}
|
||||
|
||||
function loadActiveTab() {
|
||||
var activeEl = document.getElementById('log-' + currentTab);
|
||||
if (activeEl) {
|
||||
htmx.ajax('GET', activeEl);
|
||||
loadTabEl(activeEl);
|
||||
}
|
||||
}
|
||||
|
||||
var origSwitchTab = switchTab;
|
||||
switchTab = function(tabName) {
|
||||
currentTab = tabName;
|
||||
if (typeof origSwitchTab === 'function') {
|
||||
origSwitchTab(tabName);
|
||||
}
|
||||
if (document.getElementById('auto-refresh-toggle').checked) {
|
||||
setActivePolling();
|
||||
}
|
||||
loadActiveTab();
|
||||
};
|
||||
|
||||
function toggleAutoRefresh() {
|
||||
var toggle = document.getElementById('auto-refresh-toggle');
|
||||
if (toggle.checked) {
|
||||
@@ -126,7 +124,7 @@ function toggleAutoRefresh() {
|
||||
loadActiveTab();
|
||||
} else {
|
||||
document.querySelectorAll('.log-viewer').forEach(function(el) {
|
||||
htmx.abort(el);
|
||||
if (typeof htmx !== 'undefined') htmx.abort(el);
|
||||
el.setAttribute('hx-trigger', 'none');
|
||||
});
|
||||
}
|
||||
@@ -135,5 +133,19 @@ function toggleAutoRefresh() {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadActiveTab();
|
||||
});
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var origSwitchTab = typeof switchTab === 'function' ? switchTab : null;
|
||||
switchTab = function(tabName) {
|
||||
currentTab = tabName;
|
||||
if (origSwitchTab) {
|
||||
origSwitchTab(tabName);
|
||||
}
|
||||
if (document.getElementById('auto-refresh-toggle').checked) {
|
||||
setActivePolling();
|
||||
}
|
||||
loadActiveTab();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user