diff --git a/diagnostic/index.html b/diagnostic/index.html index 303b317..c67deb8 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -1650,36 +1650,54 @@ if (msg.type === 'chat_history') { const boxes = [chatBox, document.getElementById('chat-box-fs')].filter(Boolean); for (const b of boxes) b.innerHTML = ''; + let errorCount = 0; if (msg.messages && msg.messages.length > 0) { - for (const m of msg.messages) { - if (m.type === 'aria_file') { - // ARIA-Datei-Bubble — addAriaFile schreibt selbst in beide Boxen - addAriaFile({ serverPath: m.serverPath, name: m.name, mimeType: m.mimeType, size: m.size, deleted: m.deleted }); - continue; - } - // [FILE: ...]-Marker rausfiltern (gleicher Filter wie addChat) - const cleaned = (m.text || '').replace(/\[FILE:\s*\/shared\/uploads\/[^\]]+\]/gi, '').replace(/\n{3,}/g, '\n\n').trim(); - const escaped = escapeHtml(cleaned); - let linked = linkifyText(escaped); - // /shared/uploads/-Bildpfade auch im History inline rendern - linked = linked.replace(/\/shared\/uploads\/[^\s<"]+\.(jpg|jpeg|png|gif|webp|svg|bmp)/gi, (match) => { - return `${match}`; - }); - const time = m.ts ? new Date(m.ts).toLocaleTimeString('de-DE') : '?'; - const trashBtn = m.ts - ? `` - : ''; - const innerHtml = `${trashBtn}${linked}
${escapeHtml(m.meta)} — ${time}
`; - for (const b of boxes) { - const el = document.createElement('div'); - el.className = `chat-msg ${m.type}`; - if (m.ts) el.dataset.ts = String(m.ts); - el.innerHTML = innerHtml; - b.appendChild(el); + for (let mi = 0; mi < msg.messages.length; mi++) { + const m = msg.messages[mi]; + try { + if (m.type === 'aria_file') { + addAriaFile({ serverPath: m.serverPath, name: m.name, mimeType: m.mimeType, size: m.size, deleted: m.deleted }); + continue; + } + const cleaned = (m.text || '').replace(/\[FILE:\s*\/shared\/uploads\/[^\]]+\]/gi, '').replace(/\n{3,}/g, '\n\n').trim(); + const escaped = escapeHtml(cleaned); + let linked = linkifyText(escaped); + linked = linked.replace(/\/shared\/uploads\/[^\s<"]+\.(jpg|jpeg|png|gif|webp|svg|bmp)/gi, (match) => { + return `${match}`; + }); + const time = m.ts ? new Date(m.ts).toLocaleTimeString('de-DE') : '?'; + const trashBtn = m.ts + ? `` + : ''; + const innerHtml = `${trashBtn}${linked}
${escapeHtml(m.meta)} — ${time}
`; + for (const b of boxes) { + const el = document.createElement('div'); + el.className = `chat-msg ${m.type}`; + if (m.ts) el.dataset.ts = String(m.ts); + el.innerHTML = innerHtml; + b.appendChild(el); + } + } catch (renderErr) { + // Eine kaputte Bubble darf nicht den Rest der History killen. + // Vorher passierte genau das: Frontend-Render bracht bei einer + // problematischen Antwort ab, alle nachfolgenden Nachrichten waren + // beim Reload weg. Jetzt: Fehler-Bubble einbauen + weitermachen. + errorCount++; + console.error('chat_history render error at idx ' + mi + ':', renderErr, m); + for (const b of boxes) { + const el = document.createElement('div'); + el.className = `chat-msg ${m.type || 'received'}`; + if (m.ts) el.dataset.ts = String(m.ts); + el.innerHTML = `⚠ Render-Fehler in Bubble (${escapeHtml(String(renderErr.message || renderErr))})
${m.ts ? new Date(m.ts).toLocaleTimeString('de-DE') : '?'}
`; + b.appendChild(el); + } } } for (const b of boxes) b.scrollTop = b.scrollHeight; } + if (errorCount > 0) { + console.warn(`chat_history: ${errorCount} Bubble(s) konnten nicht gerendert werden`); + } return; } diff --git a/diagnostic/server.js b/diagnostic/server.js index b9c62b5..fd5e5ff 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -701,8 +701,16 @@ function connectRVS(forcePlain) { state.rvs.lastError = err.message; broadcastState(); - // TLS Fallback - if (useTls && RVS_TLS_FALLBACK === "true" && !fallbackTriggered) { + // TLS-Fallback nur bei wirklichen TLS/Handshake-Fehlern. + // Bei Netz-Problemen wie EHOSTUNREACH, ECONNREFUSED, ENETUNREACH, + // EAI_AGAIN ist der Server eh tot — Fallback bringt nichts ausser + // Log-Spam und doppelten Retries. + const netErr = (err.code || err.message || "").toString(); + const isNetDown = + /^(EHOSTUNREACH|ECONNREFUSED|ENETUNREACH|ETIMEDOUT|EAI_AGAIN|ENOTFOUND)$/.test(netErr) || + /EHOSTUNREACH|ECONNREFUSED|ENETUNREACH|ETIMEDOUT|EAI_AGAIN|ENOTFOUND/.test(err.message || ""); + + if (useTls && RVS_TLS_FALLBACK === "true" && !fallbackTriggered && !isNetDown) { fallbackTriggered = true; log("warn", "rvs", "TLS fehlgeschlagen — Fallback auf ws://"); try { ws.removeAllListeners(); ws.close(); } catch (_) {}