fix: harden account rotation and active download recovery

Apply account enablement changes optimistically and persist rapid toggles sequentially without losing clicks.

Refresh Mega-Debrid API and Web account pools during active downloads, isolate per-account cancellation budgets, rotate within the same conversion attempt, and keep pause, stop, resume, reset, and no-account gates consistent.

Use the native Electron clipboard under Remote Desktop and make support bundle export bounded, redacted, responsive, guarded, visible, and atomic.

Add end-to-end regressions for live account switching, persisted partial recovery, support export, native copy, availability preservation, and the 500 ms telemetry cadence.
This commit is contained in:
Sucukdeluxe
2026-08-13 05:14:44 +02:00
parent ca015553f3
commit e1b4708952
26 changed files with 2811 additions and 561 deletions
+27 -16
View File
@@ -21,6 +21,7 @@ import { createRendererSettings } from "./renderer-state";
import { validateRendererSettingsUpdate } from "./renderer-settings";
import { applyMainWindowSecurity, createMainWindowWebPreferences, MAIN_WINDOW_EXTERNAL_HOSTS, openAllowedExternalUrl } from "./browser-security";
import { assertTrustedIpcSender, type TrustedIpcOptions } from "./ipc-security";
import { createSupportBundleExportRunner, writeSupportBundleAtomically } from "./support-bundle";
function validateString(value: unknown, name: string): string {
if (typeof value !== "string") {
@@ -604,13 +605,20 @@ function registerIpcHandlers(): void {
}
return controller.importQueue(json);
});
handleTrusted(IPC_CHANNELS.TOGGLE_CLIPBOARD, () => {
handleTrusted(IPC_CHANNELS.TOGGLE_CLIPBOARD, () => {
const settings = controller.getSettings();
const next = !settings.clipboardWatch;
controller.updateSettings({ clipboardWatch: next });
updateClipboardWatcher();
return next;
});
return next;
});
handleTrusted(IPC_CHANNELS.WRITE_CLIPBOARD_TEXT, (_event: IpcMainInvokeEvent, text: unknown) => {
if (typeof text !== "string" || text.length > 16 * 1024 * 1024) {
throw new Error("Ungültiger Zwischenablageinhalt");
}
clipboard.writeText(text);
return true;
});
handleTrusted(IPC_CHANNELS.PICK_FOLDER, async () => {
const options = {
properties: ["openDirectory", "createDirectory"] as Array<"openDirectory" | "createDirectory">
@@ -667,19 +675,22 @@ function registerIpcHandlers(): void {
return controller.importOnlineBackup(key);
});
handleTrusted(IPC_CHANNELS.EXPORT_SUPPORT_BUNDLE, async () => {
const options = {
defaultPath: controller.getSupportBundleDefaultFileName(),
filters: [{ name: "Support Bundle", extensions: ["zip"] }]
};
const result = mainWindow ? await dialog.showSaveDialog(mainWindow, options) : await dialog.showSaveDialog(options);
if (result.canceled || !result.filePath) {
return { saved: false };
}
const exported = await controller.exportSupportBundle();
await fs.promises.writeFile(result.filePath, exported.buffer);
return { saved: true, filePath: result.filePath };
});
const runSupportBundleExport = createSupportBundleExportRunner({
chooseFile: async () => {
const options = {
defaultPath: controller.getSupportBundleDefaultFileName(),
filters: [{ name: "Support Bundle", extensions: ["zip"] }]
};
const result = mainWindow ? await dialog.showSaveDialog(mainWindow, options) : await dialog.showSaveDialog(options);
return result.canceled || !result.filePath ? null : result.filePath;
},
build: async () => (await controller.exportSupportBundle()).buffer,
write: writeSupportBundleAtomically,
onSuccess: ({ filePath, bytes }) => controller.recordSupportBundleExported(filePath, bytes),
onFailure: (error) => controller.recordSupportBundleExportFailed(error)
});
handleTrusted(IPC_CHANNELS.EXPORT_SUPPORT_BUNDLE, () => runSupportBundleExport());
handleTrusted(IPC_CHANNELS.OPEN_LOG, async () => {
const logPath = getLogFilePath();