Files
felhom-controller/controller/internal/web/templates/app_logs.html
T
admin db52f5a4cf
gates / gates (push) Failing after 49s
v0.315.0: close the log tab's <script> — the live poller was dead, and a test now says so
FOUND IN A BROWSER. The log tab's body was lifted out of the old logs.html by line range and the
range stopped one line short, so the closing </script> never came with it. Everything downstream
agreed the page was fine:

  - html/template parsed it,
  - the page rendered and looked right,
  - TestAppPage_EachTabRendersItsOwnContent found `setInterval(window.fetchLogs` exactly where it
    expected it, because the TEXT was there.

Chrome refused the whole block with "SyntaxError: Unexpected token '<'" at the `</div>` that
followed, so the live log never refreshed. A marker proves a string is present; it does not prove
the browser will execute it.

TestAppPage_ScriptTagsAreBalanced now counts <script> against </script> on all seven app-page URLs
and checks the last block is closed. Red-proved by removing the tag again: it fails naming the logs
tab, 3 opened and 2 closed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117kRTSgs3rJWUFGxyoWN6Y
2026-10-15 19:27:21 +02:00

78 lines
2.7 KiB
HTML

{{define "app_logs"}}
{{template "layout_start" .}}
{{template "app_shell_start" .}}
{{/* „NAPLO" — today's logs page inside the shell. Its live poller runs ONLY on this tab, which is
the reason the tabs are four URLs and not four hidden panels: a poller on the settings tab
would ask the box for 200 log lines every three seconds for nothing. */}}
<div data-tab-panel="logs">
<div class="logs-container" id="logs-container">
<pre class="logs-output" id="logs-output">{{.Logs}}</pre>
</div>
<div class="logs-actions">
<span class="logs-live-indicator" id="live-indicator">
<span class="logs-live-dot"></span> {{T "logs.elo"}}
</span>
<button class="btn btn-outline btn-sm" id="logs-toggle" onclick="toggleLive()">{{T "logs.szuneteltetes"}}</button>
<button class="btn btn-outline btn-sm" onclick="fetchLogs()">{{T "logs.frissites"}}</button>
</div>
<script>
(function() {
var container = document.getElementById('logs-container');
var output = document.getElementById('logs-output');
var indicator = document.getElementById('live-indicator');
var toggleBtn = document.getElementById('logs-toggle');
var live = true;
var timer = null;
var appSlug = '{{.Meta.Slug}}';
function isAtBottom() {
return container.scrollHeight - container.scrollTop - container.clientHeight < 50;
}
window.fetchLogs = function() {
fetch('/apps/' + appSlug + '/logs?raw=1')
.then(function(r) { return r.text(); })
.then(function(text) {
var wasAtBottom = isAtBottom();
output.textContent = text;
if (wasAtBottom) container.scrollTop = container.scrollHeight;
})
.catch(function() {});
};
function startPolling() {
if (timer) return;
timer = setInterval(window.fetchLogs, 3000);
}
function stopPolling() {
if (timer) { clearInterval(timer); timer = null; }
}
window.toggleLive = function() {
live = !live;
if (live) {
startPolling();
indicator.className = 'logs-live-indicator';
indicator.innerHTML = '<span class="logs-live-dot"></span> {{T "logs.elo"}}';
toggleBtn.textContent = '{{T "logs.szuneteltetes"}}';
} else {
stopPolling();
indicator.className = 'logs-live-indicator logs-live-paused';
indicator.innerHTML = '{{T "logs.szunetelve"}}';
toggleBtn.textContent = '{{T "logs.folytatas"}}';
}
};
// Auto-scroll to bottom on initial load
container.scrollTop = container.scrollHeight;
startPolling();
})();
</script>
</div>
{{template "layout_end" .}}
{{end}}