🐛 fix: batch-done resilience, input validation, VOE JSON parse

- batch-done handler: appendHistory failure no longer prevents the
  upload-batch-done event from reaching the renderer (UI would get stuck)
- remote:input-event: validate x/y as finite numbers before passing
  to sendInputEvent (prevents NaN/Infinity crash)
- VOE upload server: wrap JSON.parse in try-catch with clear error
  message instead of raw stack trace

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Administrator 2026-03-21 11:52:13 +01:00
parent 55eee8a42e
commit 9600195954
2 changed files with 11 additions and 4 deletions

View File

@ -168,7 +168,10 @@ class VoeUploader {
} }
}); });
const body = await res.text(); const body = await res.text();
const data = JSON.parse(body); let data;
try { data = JSON.parse(body); } catch {
throw new Error(`VOE: Upload-Server Antwort war kein JSON: ${body.slice(0, 200)}`);
}
if (!data || !data.success || !data.server) { if (!data || !data.success || !data.server) {
throw new Error('VOE: Kein Upload-Server erhalten von delivery-node'); throw new Error('VOE: Kein Upload-Server erhalten von delivery-node');

10
main.js
View File

@ -716,7 +716,9 @@ ipcMain.handle('start-upload', (_event, payload) => {
uploadManager.on('batch-done', async (summary) => { uploadManager.on('batch-done', async (summary) => {
debugLog(`batch-done: total=${summary.total} ok=${summary.succeeded} fail=${summary.failed}`); debugLog(`batch-done: total=${summary.total} ok=${summary.succeeded} fail=${summary.failed}`);
await configStore.appendHistory(summary); try { await configStore.appendHistory(summary); } catch (err) {
debugLog(`appendHistory failed: ${err.message}`);
}
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('upload-batch-done', summary); mainWindow.webContents.send('upload-batch-done', summary);
} }
@ -1056,8 +1058,10 @@ ipcMain.on('remote:input-event', (_event, data) => {
const capturedH = winBounds.height - dwm; // only bottom has invisible border const capturedH = winBounds.height - dwm; // only bottom has invisible border
const contentOffsetX = contentBounds.x - (winBounds.x + dwm); const contentOffsetX = contentBounds.x - (winBounds.x + dwm);
const contentOffsetY = contentBounds.y - winBounds.y; const contentOffsetY = contentBounds.y - winBounds.y;
const x = Math.round((data.x || 0) * capturedW - contentOffsetX); const rawX = typeof data.x === 'number' && isFinite(data.x) ? data.x : 0;
const y = Math.round((data.y || 0) * capturedH - contentOffsetY); const rawY = typeof data.y === 'number' && isFinite(data.y) ? data.y : 0;
const x = Math.round(rawX * capturedW - contentOffsetX);
const y = Math.round(rawY * capturedH - contentOffsetY);
switch (data.type) { switch (data.type) {
case 'mousemove': case 'mousemove':