fix(history): remove selections atomically

This commit is contained in:
Sucukdeluxe
2026-08-21 00:59:45 +02:00
parent 3a95136ef1
commit 869dbc25be
11 changed files with 107 additions and 20 deletions
+9 -5
View File
@@ -53,7 +53,7 @@ import { getItemLogPath, initItemLogs, shutdownItemLogs } from "./item-log";
import { getPackageLogPath, initPackageLogs, shutdownPackageLogs } from "./package-log";
import { initSessionLog, getSessionLogPath, shutdownSessionLog } from "./session-log";
import { MegaWebFallback } from "./mega-web-fallback";
import { addHistoryEntry, addHistoryEntryForRetention, cancelPendingAsyncSaves, clearHistory, createStoragePaths, loadHistory, loadHistoryForRetention, loadSessionWithStatus, loadSettings, normalizeHistoryEntry, normalizeLoadedSession, normalizeLoadedSessionTransientFields, normalizeSettings, removeHistoryEntry, resetHistoryForRetention, saveHistory, saveSession, saveSettings } from "./storage";
import { addHistoryEntry, addHistoryEntryForRetention, cancelPendingAsyncSaves, clearHistory, createStoragePaths, loadHistory, loadHistoryForRetention, loadSessionWithStatus, loadSettings, normalizeHistoryEntry, normalizeLoadedSession, normalizeLoadedSessionTransientFields, normalizeSettings, removeHistoryEntries, resetHistoryForRetention, saveHistory, saveSession, saveSettings } from "./storage";
import { abortActiveUpdateDownload, checkGitHubUpdate, installLatestUpdate } from "./update";
import { runInstallWithResume } from "./update-install-flow";
import { rotateDebugToken, startDebugServer, stopDebugServer, restartDebugServer, getDebugServerRuntimeStatus, getActiveDebugToken, getDebugAllowlist, writeDebugServerConfig, clearDebugToken } from "./debug-server";
@@ -1381,10 +1381,14 @@ export class AppController {
this.manager.resetItems(itemIds);
}
public removeHistoryEntry(entryId: string): void {
this.audit("INFO", "Verlaufseintrag entfernt", { entryId });
removeHistoryEntry(this.storagePaths, entryId);
}
public removeHistoryEntry(entryId: string): void {
this.removeHistoryEntries([entryId]);
}
public removeHistoryEntries(entryIds: string[]): void {
this.audit("INFO", "Verlaufseinträge entfernt", { count: entryIds.length });
removeHistoryEntries(this.storagePaths, entryIds, this.historyLimits());
}
public addToHistory(entry: HistoryEntry): void {
this.audit("INFO", "Verlaufseintrag hinzugefügt", {
+11
View File
@@ -97,6 +97,14 @@ function isDevMode(): boolean {
return process.env.NODE_ENV === "development";
}
function validateHistoryEntryIds(value: unknown): string[] {
const entryIds = validateStringArray(value, "entryIds");
if (entryIds.length > 100_000 || entryIds.some((entryId) => entryId.length === 0 || entryId.length > 4_096)) {
throw new Error("entryIds ist ungültig");
}
return [...new Set(entryIds)];
}
function getRendererFileUrl(): string {
return pathToFileURL(path.join(app.getAppPath(), "build", "renderer", "index.html")).toString();
}
@@ -588,6 +596,9 @@ function registerIpcHandlers(): void {
validateString(entryId, "entryId");
return controller.removeHistoryEntry(entryId);
});
handleTrusted(IPC_CHANNELS.REMOVE_HISTORY_ENTRIES, (_event: IpcMainInvokeEvent, entryIds: unknown) => {
return controller.removeHistoryEntries(validateHistoryEntryIds(entryIds));
});
handleTrusted(IPC_CHANNELS.REVEAL_HISTORY_ENTRY, (_event: IpcMainInvokeEvent, entryId: unknown) => {
return revealHistoryEntry({ entryId }, {
loadHistory: () => controller.getHistory(),
+16 -6
View File
@@ -1546,12 +1546,22 @@ export function resetHistoryForRetention(paths: StoragePaths, retentionMode: His
clearHistory(paths);
}
export function removeHistoryEntry(paths: StoragePaths, entryId: string): HistoryEntry[] {
const existing = loadHistory(paths);
const updated = existing.filter(e => e.id !== entryId);
saveHistory(paths, updated);
return updated;
}
export function removeHistoryEntries(paths: StoragePaths, entryIds: readonly string[], limits?: HistoryLimits): HistoryEntry[] {
const existing = loadHistory(paths, limits);
const removedIds = new Set(entryIds);
if (removedIds.size === 0) {
return existing;
}
const updated = existing.filter((entry) => !removedIds.has(entry.id));
if (updated.length !== existing.length) {
saveHistory(paths, updated, limits);
}
return updated;
}
export function removeHistoryEntry(paths: StoragePaths, entryId: string, limits?: HistoryLimits): HistoryEntry[] {
return removeHistoryEntries(paths, [entryId], limits);
}
export function clearHistory(paths: StoragePaths): void {
ensureBaseDir(paths.baseDir);