"use strict"; function countEvidenceRecords(db) { return countRecordsInStores(db, VENDORGET_EVIDENCE_STORE_NAMES); } function countLockedEvidenceRecords(db) { return countRecordsMatching(db, VENDORGET_EVIDENCE_STORE_NAMES, (record) => { return record?.bolRecordLock === true; }); } function getEvidenceStoreCounts(db) { return new Promise((resolve, reject) => { const storeCounts = {}; const tx = db.transaction(VENDORGET_EVIDENCE_STORE_NAMES, "readonly"); tx.onerror = () => reject(tx.error); tx.onabort = () => reject(tx.error); tx.oncomplete = () => resolve(storeCounts); for (const storeName of VENDORGET_EVIDENCE_STORE_NAMES) { const countRequest = tx.objectStore(storeName).count(); countRequest.onsuccess = () => { storeCounts[storeName] = countRequest.result; }; } }); } async function purgeUnlockedEvidenceRecords(db) { const gvlWorkspaceProtection = await buildGvlWorkspaceProtectionIndex(db); return new Promise((resolve, reject) => { let deletedCount = 0; let keptLockedCount = 0; let keptGvlWorkspaceProtectedCount = 0; const tx = db.transaction(VENDORGET_EVIDENCE_STORE_NAMES, "readwrite"); tx.onerror = () => reject(tx.error); tx.onabort = () => reject(tx.error); tx.oncomplete = () => { resolve({ success: true, deletedCount, keptLockedCount, keptGvlWorkspaceProtectedCount, gvlWorkspaceProtectionNotice: keptGvlWorkspaceProtectedCount > 0 ? "Diese GVL-Evidence wurde noch nicht in den Vault exportiert." : null }); }; for (const storeName of VENDORGET_EVIDENCE_STORE_NAMES) { const cursorRequest = tx.objectStore(storeName).openCursor(); cursorRequest.onsuccess = () => { const cursor = cursorRequest.result; if (!cursor) { return; } if (cursor.value?.bolRecordLock === true) { keptLockedCount += 1; cursor.continue(); return; } if ( isGvlWorkspaceProtectedRecord( storeName, cursor.value, gvlWorkspaceProtection ) ) { keptGvlWorkspaceProtectedCount += 1; cursor.continue(); return; } deletedCount += 1; cursor.delete(); cursor.continue(); }; } }); } // TODO: GVL-Datenpflege darf nicht storeweise per clear() erfolgen. // Loeschbar ist nur eine GVL-Revision, wenn ihre zugehoerigen Raw-, Snapshot-, // Event- und normalisierten Records identifiziert sind, ihr Schutzstatus // vollstaendig bewertet wurde und eine vorhandene Vault-/Workspace-Schutzlogik // die Loeschung erlaubt. function buildGvlWorkspaceProtectionIndex(db) { return new Promise((resolve, reject) => { const protectedSnapshotSha256 = new Set(); const protectedRawGvlSha256 = new Set(); const tx = db.transaction([VENDORGET_STORE_NAMES.gvlSnapshots], "readonly"); const cursorRequest = tx .objectStore(VENDORGET_STORE_NAMES.gvlSnapshots) .openCursor(); cursorRequest.onerror = () => reject(cursorRequest.error); cursorRequest.onsuccess = () => { const cursor = cursorRequest.result; if (!cursor) { return; } const snapshot = cursor.value; const provenanceState = getGvlEvidenceProvenanceState(snapshot); if (provenanceState.workspaceDeleteProtected) { if (snapshot.sha256) { protectedSnapshotSha256.add(snapshot.sha256); } if (snapshot.rawGvlSha256) { protectedRawGvlSha256.add(snapshot.rawGvlSha256); } } cursor.continue(); }; tx.onerror = () => reject(tx.error); tx.onabort = () => reject(tx.error); tx.oncomplete = () => { resolve({ protectedSnapshotSha256, protectedRawGvlSha256 }); }; }); } function isGvlWorkspaceProtectedRecord(storeName, record, protectionIndex) { if (!record || typeof record !== "object") { return false; } if (storeName === VENDORGET_STORE_NAMES.gvlSnapshots) { return protectionIndex.protectedSnapshotSha256.has(record.sha256); } if (storeName === VENDORGET_STORE_NAMES.gvlRawEvidence) { return protectionIndex.protectedRawGvlSha256.has(record.rawGvlSha256); } if ( [ VENDORGET_STORE_NAMES.gvlVendors, VENDORGET_STORE_NAMES.gvlPurposes, VENDORGET_STORE_NAMES.gvlSpecialPurposes, VENDORGET_STORE_NAMES.gvlFeatures, VENDORGET_STORE_NAMES.gvlSpecialFeatures, VENDORGET_STORE_NAMES.gvlDataCategories, VENDORGET_STORE_NAMES.gvlVendorRelationships ].includes(storeName) ) { return protectionIndex.protectedSnapshotSha256.has(record.snapshotSha256); } return false; } function countRecordsInStores(db, storeNames) { return new Promise((resolve, reject) => { let totalCount = 0; const tx = db.transaction(storeNames, "readonly"); tx.onerror = () => reject(tx.error); tx.onabort = () => reject(tx.error); tx.oncomplete = () => resolve(totalCount); for (const storeName of storeNames) { const countRequest = tx.objectStore(storeName).count(); countRequest.onsuccess = () => { totalCount += countRequest.result; }; } }); } function countRecordsMatching(db, storeNames, predicate) { return new Promise((resolve, reject) => { let totalCount = 0; const tx = db.transaction(storeNames, "readonly"); tx.onerror = () => reject(tx.error); tx.onabort = () => reject(tx.error); tx.oncomplete = () => resolve(totalCount); for (const storeName of storeNames) { const cursorRequest = tx.objectStore(storeName).openCursor(); cursorRequest.onsuccess = () => { const cursor = cursorRequest.result; if (!cursor) { return; } if (predicate(cursor.value)) { totalCount += 1; } cursor.continue(); }; } }); }