fix(diagnostic): chat_history-Render verträgt kaputte Bubbles + EHOSTUNREACH skipped TLS-Fallback
Zwei kleine Robustness-Verbesserungen: 1) chat_history-Handler im Frontend: jede Bubble jetzt in try/catch. Wenn eine Bubble bei der Render-Pipeline (escape/linkify/regex-replace) eine Exception wirft, brach die ganze for-Schleife ab und alle nachfolgenden Bubbles wurden nicht mehr in den DOM geschrieben — beim Reload sah man dann nur die ersten N Eintraege und Stefan dachte die letzten Antworten waeren weg. Jetzt: Fehler-Bubble mit "⚠ Render-Fehler" + console.error, restliche Bubbles laufen weiter durch. 2) Diagnostic-Server RVS-Reconnect: TLS-Fallback war auch bei reinen Netz-Fehlern (EHOSTUNREACH, ECONNREFUSED, ENETUNREACH, ETIMEDOUT, ENOTFOUND, EAI_AGAIN) gefeuert — bringt nichts weil der Server eh tot ist, generiert aber doppelte Reconnect-Versuche + Log-Spam. Jetzt nur noch bei wirklichen TLS/Handshake-Fehlern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+43
-25
@@ -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 `<a href="${match}" target="_blank">${match}</a><img src="${match}" class="chat-media" onclick="openLightbox('image','${match}')" onerror="this.style.display='none'">`;
|
||||
});
|
||||
const time = m.ts ? new Date(m.ts).toLocaleTimeString('de-DE') : '?';
|
||||
const trashBtn = m.ts
|
||||
? `<button class="bubble-trash" title="Diese Bubble loeschen" onclick="deleteDiagBubble(${m.ts})">🗑</button>`
|
||||
: '';
|
||||
const innerHtml = `${trashBtn}${linked}<div class="meta">${escapeHtml(m.meta)} — ${time}</div>`;
|
||||
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 `<a href="${match}" target="_blank">${match}</a><img src="${match}" class="chat-media" onclick="openLightbox('image','${match}')" onerror="this.style.display='none'">`;
|
||||
});
|
||||
const time = m.ts ? new Date(m.ts).toLocaleTimeString('de-DE') : '?';
|
||||
const trashBtn = m.ts
|
||||
? `<button class="bubble-trash" title="Diese Bubble loeschen" onclick="deleteDiagBubble(${m.ts})">🗑</button>`
|
||||
: '';
|
||||
const innerHtml = `${trashBtn}${linked}<div class="meta">${escapeHtml(m.meta)} — ${time}</div>`;
|
||||
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 = `<span style="color:#FF6B6B;">⚠ Render-Fehler in Bubble (${escapeHtml(String(renderErr.message || renderErr))})</span><div class="meta">${m.ts ? new Date(m.ts).toLocaleTimeString('de-DE') : '?'}</div>`;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+10
-2
@@ -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 (_) {}
|
||||
|
||||
Reference in New Issue
Block a user