From 52b2e0a1e47da0ccae230a57f554ba0b4356fc3c Mon Sep 17 00:00:00 2001 From: Administrator Date: Tue, 10 Mar 2026 14:11:03 +0100 Subject: [PATCH] fix: vidmoly redirect loop, body leak, update error handling, submenu overflow - Add max redirect depth (10) to Vidmoly _fetch to prevent stack overflow - Drain undici response body on redirect to prevent connection leaks - Fix installUpdate unhandled promise rejection in main.js - Fix context menu submenu viewport overflow with flip-left CSS Co-Authored-By: Claude Opus 4.6 --- lib/vidmoly-upload.js | 14 +++++++++++--- main.js | 22 +++++++++++----------- renderer/app.js | 8 +++++++- renderer/styles.css | 3 +++ 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/lib/vidmoly-upload.js b/lib/vidmoly-upload.js index cfda9e0..54952df 100644 --- a/lib/vidmoly-upload.js +++ b/lib/vidmoly-upload.js @@ -45,7 +45,8 @@ class VidmolyUploader { /** * Simple GET/POST using built-in fetch (handles redirects) */ - async _fetch(url, opts = {}) { + async _fetch(url, opts = {}, _redirectCount = 0) { + const MAX_REDIRECTS = 10; const headers = { 'User-Agent': USER_AGENT, ...(opts.headers || {}) @@ -64,10 +65,15 @@ class VidmolyUploader { // Follow redirects manually (to capture cookies at each hop) if ([301, 302, 303, 307, 308].includes(res.status)) { + // Drain body to prevent connection leak + try { await res.text(); } catch {} + if (_redirectCount >= MAX_REDIRECTS) { + throw new Error('Zu viele Redirects'); + } const location = res.headers.get('location'); if (location) { const nextUrl = new URL(location, url).href; - return this._fetch(nextUrl, { ...opts, method: 'GET', body: undefined }); + return this._fetch(nextUrl, { ...opts, method: 'GET', body: undefined }, _redirectCount + 1); } } @@ -240,11 +246,13 @@ class VidmolyUploader { let resultHtml; if ([301, 302, 303].includes(statusCode)) { const location = headers && headers.location; + // Always drain the original body to prevent connection leak + try { await body.text(); } catch {} if (location) { const resultRes = await this._fetch(new URL(location, uploadUrl).href); resultHtml = await resultRes.text(); } else { - resultHtml = await body.text(); + resultHtml = ''; } } else { resultHtml = await body.text(); diff --git a/main.js b/main.js index 215d2e1..18a9280 100644 --- a/main.js +++ b/main.js @@ -349,17 +349,17 @@ ipcMain.handle('app:check-updates', async () => { } }); -ipcMain.handle('app:install-update', async () => { - try { - installUpdate((progress) => { - if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.webContents.send('app:update-progress', progress); - } - }); - return { started: true }; - } catch (err) { - return { error: err.message }; - } +ipcMain.handle('app:install-update', () => { + installUpdate((progress) => { + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.send('app:update-progress', progress); + } + }).catch((err) => { + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.send('app:update-progress', { stage: 'error', error: err.message }); + } + }); + return { started: true }; }); ipcMain.handle('app:abort-update', () => { diff --git a/renderer/app.js b/renderer/app.js index eeef89f..0567987 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -342,8 +342,14 @@ function showContextMenu(x, y) { if (aotItem) aotItem.textContent = alwaysOnTopState ? 'Immer im Vordergrund ✓' : 'Immer im Vordergrund'; menu.style.display = 'block'; - menu.style.left = Math.min(x, window.innerWidth - menu.offsetWidth - 5) + 'px'; + const menuX = Math.min(x, window.innerWidth - menu.offsetWidth - 5); + menu.style.left = menuX + 'px'; menu.style.top = Math.min(y, window.innerHeight - menu.offsetHeight - 5) + 'px'; + + // Flip submenus if they would overflow the viewport right edge + menu.querySelectorAll('.ctx-submenu-items').forEach(sub => { + sub.classList.toggle('flip-left', menuX + menu.offsetWidth + sub.offsetWidth > window.innerWidth); + }); } function hideContextMenu() { diff --git a/renderer/styles.css b/renderer/styles.css index 0fd09a5..2596d7d 100644 --- a/renderer/styles.css +++ b/renderer/styles.css @@ -314,8 +314,11 @@ body { border-radius: 6px; padding: 4px 0; min-width: 160px; + max-width: calc(100vw - 16px); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6); } +/* Flip submenu to the left when it would overflow the viewport */ +.ctx-submenu-items.flip-left { left: auto; right: 100%; } .ctx-submenu:hover .ctx-submenu-items { display: block; } /* Settings View */