Fix logs tab stuck on Loading after switching away and back.
Release rolling / release (push) Successful in 9m0s

This commit is contained in:
Raven Scott
2026-07-16 00:45:30 -04:00
parent 8f0599eeae
commit d697f6f90d
2 changed files with 300 additions and 133 deletions
+26 -10
View File
@@ -91,13 +91,20 @@ async function startLogStream(session, streams, args) {
const containerId = args.id || args.containerId
if (!containerId) throw new Error('container id required')
if (streams.has(containerId)) {
// Tear down any existing stream for this container first. Mark it dead so
// late error/end events from the old stream cannot clear the new entry.
const prev = streams.get(containerId)
if (prev) {
prev.dead = true
try {
streams.get(containerId).stream?.destroy?.()
prev.stream?.removeAllListeners?.()
prev.stream?.destroy?.()
} catch {
// ignore
}
streams.delete(containerId)
// Brief yield so docker-modem can release the hijacked socket before re-attach
await new Promise((r) => setTimeout(r, 50))
}
const logOpts = {
@@ -112,10 +119,13 @@ async function startLogStream(session, streams, args) {
const logsStream = await docker.getContainer(containerId).logs(logOpts)
const demux = createServerDemuxer()
const gen = (prev?.gen || 0) + 1
const entry = { stream: logsStream, demux, gen, dead: false }
streams.set(containerId, { stream: logsStream, demux })
streams.set(containerId, entry)
logsStream.on('data', (chunk) => {
if (entry.dead || streams.get(containerId) !== entry) return
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
const payloads = demux.push(buf)
for (const payload of payloads) {
@@ -134,6 +144,7 @@ async function startLogStream(session, streams, args) {
}
})
logsStream.on('end', () => {
if (entry.dead) return
for (const payload of demux.flush()) {
if (!payload.length) continue
try {
@@ -148,18 +159,23 @@ async function startLogStream(session, streams, args) {
// ignore
}
}
streams.delete(containerId)
if (streams.get(containerId) === entry) streams.delete(containerId)
})
logsStream.on('error', (err) => {
if (entry.dead) return
logger.error('Log stream error', { containerId, error: err.message })
session.push(Pushes.error, {
error: `Log stream error: ${err.message}`,
containerId,
})
streams.delete(containerId)
try {
session.push(Pushes.error, {
error: `Log stream error: ${err.message}`,
containerId,
})
} catch {
// ignore
}
if (streams.get(containerId) === entry) streams.delete(containerId)
})
return { success: true, message: `Log stream started for ${containerId}` }
return { success: true, message: `Log stream started for ${containerId}`, containerId }
}
export function registerLogsHandlers(session) {