Persist collector state and stabilize download controls
Persist collector packages and disclosure state through validated atomic AppData storage with backup recovery and shutdown synchronization. Align download sidebar metrics with the hidden-extracted presentation scope and keep Start available through temporary Real-Debrid cooldowns while preserving hard account blocks.
This commit is contained in:
@@ -47,6 +47,11 @@ export interface CollectorInspectionResult {
|
||||
duplicateCount: number;
|
||||
}
|
||||
|
||||
export interface CollectorPersistenceState {
|
||||
packages: CollectorPackage[];
|
||||
collapsedPackageIds: string[];
|
||||
}
|
||||
|
||||
function validAddedAt(value: unknown): value is number {
|
||||
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
||||
}
|
||||
@@ -147,6 +152,31 @@ export function validateCollectorEnrichmentRequest(value: unknown): CollectorEnr
|
||||
return { requestId: raw.requestId, packages: structuredClone(raw.packages) as CollectorPackage[] };
|
||||
}
|
||||
|
||||
export function validateCollectorPersistenceState(value: unknown): CollectorPersistenceState {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw new Error("Linksammler-Speicherzustand ist ungültig");
|
||||
}
|
||||
const raw = value as Record<string, unknown>;
|
||||
const packages = raw.packages;
|
||||
const collapsedPackageIds = raw.collapsedPackageIds;
|
||||
if (Object.keys(raw).some((key) => key !== "packages" && key !== "collapsedPackageIds")
|
||||
|| !Array.isArray(packages)
|
||||
|| packages.length > 2_000
|
||||
|| packages.some((entry) => !validCollectorPackage(entry))
|
||||
|| packages.reduce((sum, entry) => sum + (entry as CollectorPackage).links.length, 0) > 20_000
|
||||
|| !Array.isArray(collapsedPackageIds)
|
||||
|| collapsedPackageIds.length > 2_000
|
||||
|| collapsedPackageIds.some((entry) => typeof entry !== "string" || entry.length === 0 || entry.length > 160)) {
|
||||
throw new Error("Linksammler-Speicherzustand ist ungültig");
|
||||
}
|
||||
const packageIds = new Set(packages.map((pkg) => (pkg as CollectorPackage).id));
|
||||
const collapsed = [...new Set(collapsedPackageIds)].filter((id) => packageIds.has(id));
|
||||
return {
|
||||
packages: structuredClone(packages) as CollectorPackage[],
|
||||
collapsedPackageIds: collapsed
|
||||
};
|
||||
}
|
||||
|
||||
function collectorMarkerValue(value: string): string {
|
||||
return String(value || "").replace(/[\r\n]+/g, " ").trim();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ export const IPC_CHANNELS = {
|
||||
PREPARE_COLLECTOR_CONTAINERS: "collector:prepare-containers",
|
||||
ENRICH_COLLECTOR_PACKAGES: "collector:enrich-packages",
|
||||
COLLECTOR_ENRICHMENT_PROGRESS: "collector:enrichment-progress",
|
||||
GET_COLLECTOR_STATE: "collector:get-state",
|
||||
SAVE_COLLECTOR_STATE: "collector:save-state",
|
||||
SAVE_COLLECTOR_STATE_SYNC: "collector:save-state-sync",
|
||||
GET_START_CONFLICTS: "queue:get-start-conflicts",
|
||||
RESOLVE_START_CONFLICT: "queue:resolve-start-conflict",
|
||||
CLEAR_ALL: "queue:clear-all",
|
||||
|
||||
@@ -38,6 +38,7 @@ import type {
|
||||
CollectorEnrichmentRequest,
|
||||
CollectorEnrichmentProgress,
|
||||
CollectorInspectionResult,
|
||||
CollectorPersistenceState,
|
||||
CollectorTextPreparationRequest
|
||||
} from "./collector";
|
||||
|
||||
@@ -89,6 +90,9 @@ export interface ElectronApi {
|
||||
prepareCollectorContainers: (filePaths: string[], addedAt: number) => Promise<CollectorInspectionResult>;
|
||||
enrichCollectorPackages: (request: CollectorEnrichmentRequest) => Promise<CollectorInspectionResult>;
|
||||
onCollectorEnrichmentProgress: (callback: (progress: CollectorEnrichmentProgress) => void) => () => void;
|
||||
getCollectorState: () => Promise<CollectorPersistenceState>;
|
||||
saveCollectorState: (state: CollectorPersistenceState) => Promise<CollectorPersistenceState>;
|
||||
saveCollectorStateSync: (state: CollectorPersistenceState) => void;
|
||||
getPathForDroppedFile: (file: File) => string;
|
||||
getStartConflicts: () => Promise<StartConflictEntry[]>;
|
||||
resolveStartConflict: (packageId: string, policy: DuplicatePolicy) => Promise<StartConflictResolutionResult>;
|
||||
|
||||
Reference in New Issue
Block a user