|
|
|
|
@ -227,11 +227,51 @@ function rotLog(msg, ts) {
|
|
|
|
|
} catch {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Catch unhandled rejections from fire-and-forget async calls
|
|
|
|
|
function _writeCrashLog(prefix, err, extra) {
|
|
|
|
|
try {
|
|
|
|
|
const ts = new Date().toISOString();
|
|
|
|
|
const line = `[${ts}] ${prefix} ${err && err.stack ? err.stack : (err && err.message) || String(err)}${extra ? ' :: ' + JSON.stringify(extra) : ''}\n`;
|
|
|
|
|
try {
|
|
|
|
|
const target = getDebugLogPath();
|
|
|
|
|
fs.appendFileSync(target, line, 'utf-8');
|
|
|
|
|
} catch {}
|
|
|
|
|
try {
|
|
|
|
|
const crashDir = path.dirname(getDebugLogPath());
|
|
|
|
|
fs.appendFileSync(path.join(crashDir, 'crash.log'), line, 'utf-8');
|
|
|
|
|
} catch {}
|
|
|
|
|
} catch {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
process.on('unhandledRejection', (reason) => {
|
|
|
|
|
debugLog(`UNHANDLED REJECTION: ${reason && reason.stack ? reason.stack : reason}`);
|
|
|
|
|
_writeCrashLog('UNHANDLED REJECTION', reason);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
process.on('uncaughtException', (err, origin) => {
|
|
|
|
|
_writeCrashLog('UNCAUGHT EXCEPTION (' + origin + ')', err);
|
|
|
|
|
debugLog(`UNCAUGHT EXCEPTION (${origin}): ${err && err.stack ? err.stack : err}`);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
process.on('exit', (code) => {
|
|
|
|
|
try { _writeCrashLog('PROCESS EXIT', new Error('code=' + code)); } catch {}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
process.on('warning', (warning) => {
|
|
|
|
|
debugLog(`PROCESS WARNING: ${warning.name} ${warning.message}`);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (const sig of ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGBREAK']) {
|
|
|
|
|
try {
|
|
|
|
|
process.on(sig, () => {
|
|
|
|
|
_writeCrashLog('SIGNAL ' + sig, new Error('process received ' + sig));
|
|
|
|
|
try {
|
|
|
|
|
if (_debugLogBuffer.length) fs.appendFileSync(getDebugLogPath(), _debugLogBuffer.join(''), 'utf-8');
|
|
|
|
|
} catch {}
|
|
|
|
|
process.exit(0);
|
|
|
|
|
});
|
|
|
|
|
} catch {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function withTimeout(promise, timeoutMs, label) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
|
@ -947,6 +987,51 @@ function createWindow() {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mainWindow.webContents.setBackgroundThrottling(false);
|
|
|
|
|
|
|
|
|
|
mainWindow.webContents.on('render-process-gone', (_event, details) => {
|
|
|
|
|
_writeCrashLog('RENDER PROCESS GONE', new Error(details.reason || 'unknown'), details);
|
|
|
|
|
debugLog(`RENDER PROCESS GONE: reason=${details.reason} exitCode=${details.exitCode}`);
|
|
|
|
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
|
|
|
try {
|
|
|
|
|
const choice = dialog.showMessageBoxSync(mainWindow, {
|
|
|
|
|
type: 'error',
|
|
|
|
|
title: 'Renderer abgestürzt',
|
|
|
|
|
message: `Der Renderer-Prozess ist abgestürzt (${details.reason}).`,
|
|
|
|
|
detail: 'Bitte Diagnose-Paket exportieren und einsenden. Klick "Neu laden" um die UI wiederherzustellen — laufende Uploads im Main-Process bleiben aktiv.',
|
|
|
|
|
buttons: ['Neu laden', 'Beenden'],
|
|
|
|
|
defaultId: 0,
|
|
|
|
|
cancelId: 1
|
|
|
|
|
});
|
|
|
|
|
if (choice === 0) {
|
|
|
|
|
mainWindow.webContents.reload();
|
|
|
|
|
} else {
|
|
|
|
|
app.exit(1);
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
try { mainWindow.webContents.reload(); } catch {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mainWindow.webContents.on('unresponsive', () => {
|
|
|
|
|
_writeCrashLog('RENDERER UNRESPONSIVE', new Error('webContents unresponsive'));
|
|
|
|
|
debugLog('RENDERER UNRESPONSIVE');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mainWindow.webContents.on('responsive', () => {
|
|
|
|
|
debugLog('RENDERER RESPONSIVE AGAIN');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDescription, validatedURL) => {
|
|
|
|
|
_writeCrashLog('DID-FAIL-LOAD', new Error(errorDescription), { errorCode, validatedURL });
|
|
|
|
|
debugLog(`DID-FAIL-LOAD: ${errorCode} ${errorDescription} url=${validatedURL}`);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.on('child-process-gone', (_event, details) => {
|
|
|
|
|
_writeCrashLog('CHILD PROCESS GONE', new Error(details.reason || 'unknown'), details);
|
|
|
|
|
debugLog(`CHILD PROCESS GONE: type=${details.type} reason=${details.reason} exitCode=${details.exitCode}`);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mainWindow.loadFile(path.join(__dirname, 'renderer', 'index.html'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -1049,6 +1134,9 @@ app.whenReady().then(() => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.on('window-all-closed', () => {
|
|
|
|
|
const activeJobs = uploadManager && typeof uploadManager.getActiveJobCount === 'function' ? uploadManager.getActiveJobCount() : 0;
|
|
|
|
|
debugLog(`window-all-closed: activeJobs=${activeJobs}, uploadManager=${!!uploadManager}`);
|
|
|
|
|
_writeCrashLog('WINDOW-ALL-CLOSED', new Error('all windows closed'), { activeJobs, uploadManager: !!uploadManager });
|
|
|
|
|
app.quit();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|