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 <noreply@anthropic.com>
This commit is contained in:
parent
9c56fabce1
commit
52b2e0a1e4
@ -45,7 +45,8 @@ class VidmolyUploader {
|
|||||||
/**
|
/**
|
||||||
* Simple GET/POST using built-in fetch (handles redirects)
|
* 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 = {
|
const headers = {
|
||||||
'User-Agent': USER_AGENT,
|
'User-Agent': USER_AGENT,
|
||||||
...(opts.headers || {})
|
...(opts.headers || {})
|
||||||
@ -64,10 +65,15 @@ class VidmolyUploader {
|
|||||||
|
|
||||||
// Follow redirects manually (to capture cookies at each hop)
|
// Follow redirects manually (to capture cookies at each hop)
|
||||||
if ([301, 302, 303, 307, 308].includes(res.status)) {
|
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');
|
const location = res.headers.get('location');
|
||||||
if (location) {
|
if (location) {
|
||||||
const nextUrl = new URL(location, url).href;
|
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;
|
let resultHtml;
|
||||||
if ([301, 302, 303].includes(statusCode)) {
|
if ([301, 302, 303].includes(statusCode)) {
|
||||||
const location = headers && headers.location;
|
const location = headers && headers.location;
|
||||||
|
// Always drain the original body to prevent connection leak
|
||||||
|
try { await body.text(); } catch {}
|
||||||
if (location) {
|
if (location) {
|
||||||
const resultRes = await this._fetch(new URL(location, uploadUrl).href);
|
const resultRes = await this._fetch(new URL(location, uploadUrl).href);
|
||||||
resultHtml = await resultRes.text();
|
resultHtml = await resultRes.text();
|
||||||
} else {
|
} else {
|
||||||
resultHtml = await body.text();
|
resultHtml = '';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resultHtml = await body.text();
|
resultHtml = await body.text();
|
||||||
|
|||||||
22
main.js
22
main.js
@ -349,17 +349,17 @@ ipcMain.handle('app:check-updates', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('app:install-update', async () => {
|
ipcMain.handle('app:install-update', () => {
|
||||||
try {
|
installUpdate((progress) => {
|
||||||
installUpdate((progress) => {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
mainWindow.webContents.send('app:update-progress', progress);
|
||||||
mainWindow.webContents.send('app:update-progress', progress);
|
}
|
||||||
}
|
}).catch((err) => {
|
||||||
});
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
return { started: true };
|
mainWindow.webContents.send('app:update-progress', { stage: 'error', error: err.message });
|
||||||
} catch (err) {
|
}
|
||||||
return { error: err.message };
|
});
|
||||||
}
|
return { started: true };
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('app:abort-update', () => {
|
ipcMain.handle('app:abort-update', () => {
|
||||||
|
|||||||
@ -342,8 +342,14 @@ function showContextMenu(x, y) {
|
|||||||
if (aotItem) aotItem.textContent = alwaysOnTopState ? 'Immer im Vordergrund ✓' : 'Immer im Vordergrund';
|
if (aotItem) aotItem.textContent = alwaysOnTopState ? 'Immer im Vordergrund ✓' : 'Immer im Vordergrund';
|
||||||
|
|
||||||
menu.style.display = 'block';
|
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';
|
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() {
|
function hideContextMenu() {
|
||||||
|
|||||||
@ -314,8 +314,11 @@ body {
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
min-width: 160px;
|
min-width: 160px;
|
||||||
|
max-width: calc(100vw - 16px);
|
||||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
|
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; }
|
.ctx-submenu:hover .ctx-submenu-items { display: block; }
|
||||||
|
|
||||||
/* Settings View */
|
/* Settings View */
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user