fix(989): correct four live build-log viewer bugs in logs.js
- live detection: use !h.status (null while running) not === 'running'
- SSE: replace named addEventListener('chunk'/'done') with onmessage +
frame.done check — backend sends generic message events, no named type
- SSE payload: use frame.stdout_append/stderr_append not frame.text
- timestamps: started_at/finished_at are unix seconds, multiply by 1000
was already done implicitly — actually use unix arithmetic directly
to avoid new Date() confusion entirely
Also move the misplaced .flow-main-slim rule from the logs section to
the flow-shell section of dashboard.css.
This commit is contained in:
parent
0b15cad93f
commit
ee61eac663
2 changed files with 43 additions and 27 deletions
|
|
@ -1805,6 +1805,11 @@ body.flow-shell .tabbar .tab.active.tab-link {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
/* flow.html now has a slim header (back link only) instead of the full
|
||||||
|
tabbar — reduce the top padding to match the smaller header height. */
|
||||||
|
body.flow-shell .flow-main-slim {
|
||||||
|
padding-top: calc(var(--flow-header-h) + 0.4em);
|
||||||
|
}
|
||||||
.flow-main .terminal-wrap {
|
.flow-main .terminal-wrap {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
|
|
@ -1864,9 +1869,6 @@ body.logs-shell {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
body.flow-shell .flow-main-slim {
|
|
||||||
padding-top: calc(var(--flow-header-h) + 0.4em);
|
|
||||||
}
|
|
||||||
|
|
||||||
.logs-header {
|
.logs-header {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
|
|
||||||
|
|
@ -40,13 +40,11 @@ import {
|
||||||
|
|
||||||
// ─── helpers ──────────────────────────────────────────────────────────
|
// ─── helpers ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function fmtTs(isoStr) {
|
// started_at / finished_at are unix seconds (i64), not milliseconds.
|
||||||
if (!isoStr) return '';
|
function fmtTs(unixSecs) {
|
||||||
try {
|
if (!unixSecs) return '';
|
||||||
const d = new Date(isoStr);
|
const age = Math.floor(Date.now() / 1000) - unixSecs;
|
||||||
const age = Math.floor((Date.now() - d.getTime()) / 1000);
|
return fmtAgeSecs(Math.max(0, age)) + ' ago';
|
||||||
return fmtAgeSecs(age) + ' ago';
|
|
||||||
} catch { return isoStr; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fmtDuration(secs) {
|
function fmtDuration(secs) {
|
||||||
|
|
@ -76,10 +74,11 @@ import {
|
||||||
const ul = el('ul', { class: 'build-logs-list' });
|
const ul = el('ul', { class: 'build-logs-list' });
|
||||||
for (const h of rows) {
|
for (const h of rows) {
|
||||||
const li = el('li', { class: 'build-logs-item' });
|
const li = el('li', { class: 'build-logs-item' });
|
||||||
|
// status is null while running, 'ok'/'fail' when finished.
|
||||||
|
const live = !h.status;
|
||||||
const ok = h.status === 'ok';
|
const ok = h.status === 'ok';
|
||||||
const live = h.status === 'running';
|
const statusClass = live ? 'badge badge-running' : ok ? 'badge badge-ok' : 'badge badge-fail';
|
||||||
const statusClass = ok ? 'build-logs-ok' : live ? 'build-logs-live-badge badge badge-running' : 'build-logs-fail';
|
const statusLabel = live ? 'live' : ok ? 'ok' : 'fail';
|
||||||
const statusLabel = ok ? '✓' : live ? 'live' : '✗';
|
|
||||||
const age = h.finished_at ? fmtTs(h.finished_at) : (live ? '' : fmtTs(h.started_at));
|
const age = h.finished_at ? fmtTs(h.finished_at) : (live ? '' : fmtTs(h.started_at));
|
||||||
const runtime = h.runtime_secs != null
|
const runtime = h.runtime_secs != null
|
||||||
? el('span', { class: 'build-logs-runtime meta' }, fmtDuration(Math.max(0, h.runtime_secs)))
|
? el('span', { class: 'build-logs-runtime meta' }, fmtDuration(Math.max(0, h.runtime_secs)))
|
||||||
|
|
@ -125,20 +124,35 @@ import {
|
||||||
const badge = el('span', { class: 'build-logs-live-badge badge badge-running' }, 'live');
|
const badge = el('span', { class: 'build-logs-live-badge badge badge-running' }, 'live');
|
||||||
detail.append(badge, pre);
|
detail.append(badge, pre);
|
||||||
streamEs = new EventSource('/api/build-logs/id/' + h.id + '/stream');
|
streamEs = new EventSource('/api/build-logs/id/' + h.id + '/stream');
|
||||||
streamEs.addEventListener('chunk', (e) => {
|
let stdoutLen = 0, stderrLen = 0;
|
||||||
try { pre.textContent += JSON.parse(e.data).text; } catch { /**/ }
|
streamEs.onmessage = (e) => {
|
||||||
});
|
let frame;
|
||||||
streamEs.addEventListener('done', () => {
|
try { frame = JSON.parse(e.data); } catch { return; }
|
||||||
badge.textContent = 'done';
|
if (frame.stdout_append) {
|
||||||
badge.className = 'build-logs-live-badge badge';
|
pre.textContent += frame.stdout_append;
|
||||||
streamEs.close(); streamEs = null;
|
stdoutLen += frame.stdout_append.length;
|
||||||
detail.dataset.loaded = '1';
|
}
|
||||||
});
|
if (frame.stderr_append) {
|
||||||
streamEs.addEventListener('error', () => {
|
if (stderrLen === 0) pre.textContent += '\n--- stderr ---\n';
|
||||||
badge.textContent = 'stream error';
|
pre.textContent += frame.stderr_append;
|
||||||
badge.className = 'build-logs-live-badge badge';
|
stderrLen += frame.stderr_append.length;
|
||||||
streamEs.close(); streamEs = null;
|
}
|
||||||
});
|
if (frame.done) {
|
||||||
|
badge.className = frame.status === 'ok' ? 'badge badge-ok' : 'badge badge-fail';
|
||||||
|
badge.textContent = frame.status || 'done';
|
||||||
|
streamEs.close(); streamEs = null;
|
||||||
|
detail.dataset.loaded = '1';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
streamEs.onerror = () => {
|
||||||
|
if (streamEs && streamEs.readyState === EventSource.CONNECTING) {
|
||||||
|
pre.textContent = ''; stdoutLen = 0; stderrLen = 0;
|
||||||
|
} else {
|
||||||
|
badge.textContent = 'stream error';
|
||||||
|
badge.className = 'badge badge-fail';
|
||||||
|
if (streamEs) { streamEs.close(); streamEs = null; }
|
||||||
|
}
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
const pre = el('pre', { class: 'build-logs-output' }, 'fetching…');
|
const pre = el('pre', { class: 'build-logs-output' }, 'fetching…');
|
||||||
detail.append(pre);
|
detail.append(pre);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue