Consolidate GVL deletion workflow and document purge semantics
Dieser Commit ist enthalten in:
+2
-11
@@ -125,10 +125,6 @@ async function handleVendorGetMessage(message, sender) {
|
||||
return handlePurgeUnlockedEvidenceRecordsMessage();
|
||||
}
|
||||
|
||||
if (message.type === "purge_gvl_reference_data") {
|
||||
return handlePurgeGvlReferenceDataMessage();
|
||||
}
|
||||
|
||||
if (message.type === "delete_all_evidence_database") {
|
||||
return handleDeleteAllEvidenceDatabaseMessage();
|
||||
}
|
||||
@@ -302,7 +298,8 @@ async function handleMarkGvlRevisionEvidenceVaultCopyMessage(message) {
|
||||
return {
|
||||
success: true,
|
||||
mark: await markVendorGetGvlRevisionEvidenceVaultCopy(
|
||||
message?.payload?.snapshotSha256 ?? null
|
||||
message?.payload?.snapshotSha256 ?? null,
|
||||
message?.payload?.verification ?? null
|
||||
)
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -1229,12 +1226,6 @@ async function handlePurgeUnlockedEvidenceRecordsMessage() {
|
||||
return purgeUnlockedEvidenceRecords(db);
|
||||
}
|
||||
|
||||
async function handlePurgeGvlReferenceDataMessage() {
|
||||
const db = await openVendorGetDb();
|
||||
|
||||
return purgeGvlReferenceData(db);
|
||||
}
|
||||
|
||||
function handleDeleteAllEvidenceDatabaseMessage() {
|
||||
return deleteVendorGetDatabase();
|
||||
}
|
||||
|
||||
@@ -33,16 +33,3 @@ const VENDORGET_EVIDENCE_STORE_NAMES = [
|
||||
VENDORGET_STORE_NAMES.gvlDataCategories,
|
||||
VENDORGET_STORE_NAMES.gvlVendorRelationships
|
||||
];
|
||||
|
||||
const VENDORGET_GVL_REFERENCE_STORE_NAMES = [
|
||||
VENDORGET_STORE_NAMES.gvlRawEvidence,
|
||||
VENDORGET_STORE_NAMES.gvlSnapshots,
|
||||
VENDORGET_STORE_NAMES.gvlSnapshotEvents,
|
||||
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
|
||||
];
|
||||
|
||||
@@ -89,31 +89,11 @@ async function purgeUnlockedEvidenceRecords(db) {
|
||||
});
|
||||
}
|
||||
|
||||
function purgeGvlReferenceData(db) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const clearedStores = {};
|
||||
const tx = db.transaction(VENDORGET_GVL_REFERENCE_STORE_NAMES, "readwrite");
|
||||
|
||||
tx.onerror = () => reject(tx.error);
|
||||
tx.onabort = () => reject(tx.error);
|
||||
tx.oncomplete = () => {
|
||||
resolve({
|
||||
success: true,
|
||||
clearedStores
|
||||
});
|
||||
};
|
||||
|
||||
for (const storeName of VENDORGET_GVL_REFERENCE_STORE_NAMES) {
|
||||
const objectStore = tx.objectStore(storeName);
|
||||
const countRequest = objectStore.count();
|
||||
|
||||
countRequest.onsuccess = () => {
|
||||
clearedStores[storeName] = countRequest.result;
|
||||
objectStore.clear();
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
// 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) => {
|
||||
|
||||
@@ -287,14 +287,21 @@ async function importVendorGetGvlRevisionEvidenceJson(exportContainer) {
|
||||
};
|
||||
}
|
||||
|
||||
async function markVendorGetGvlRevisionEvidenceVaultCopy(snapshotSha256) {
|
||||
async function markVendorGetGvlRevisionEvidenceVaultCopy(
|
||||
snapshotSha256,
|
||||
verification = null
|
||||
) {
|
||||
if (!snapshotSha256) {
|
||||
throw new Error("missing_snapshot_sha256");
|
||||
}
|
||||
|
||||
const db = await openVendorGetDb();
|
||||
|
||||
return markGvlRevisionEvidenceVaultCopyAvailable(db, snapshotSha256);
|
||||
return markGvlRevisionEvidenceVaultCopyAvailable(
|
||||
db,
|
||||
snapshotSha256,
|
||||
verification
|
||||
);
|
||||
}
|
||||
|
||||
function getGvlEvidenceRecordByKey(db, storeName, key) {
|
||||
@@ -632,9 +639,16 @@ function formatGvlEvidenceProvenance(values) {
|
||||
return "web";
|
||||
}
|
||||
|
||||
function markGvlRevisionEvidenceVaultCopyAvailable(db, snapshotSha256) {
|
||||
return updateGvlRevisionEvidenceRecords(db, snapshotSha256, (record) =>
|
||||
markGvlEvidenceRecordVaultCopyAvailable(record)
|
||||
function markGvlRevisionEvidenceVaultCopyAvailable(
|
||||
db,
|
||||
snapshotSha256,
|
||||
verification = null
|
||||
) {
|
||||
return updateGvlRevisionEvidenceRecords(
|
||||
db,
|
||||
snapshotSha256,
|
||||
(record) => markGvlEvidenceRecordVaultCopyAvailable(record),
|
||||
verification
|
||||
);
|
||||
}
|
||||
|
||||
@@ -652,7 +666,12 @@ function markGvlRevisionEvidenceProvenance(db, snapshotSha256, provenance) {
|
||||
);
|
||||
}
|
||||
|
||||
function updateGvlRevisionEvidenceRecords(db, snapshotSha256, updateRecord) {
|
||||
function updateGvlRevisionEvidenceRecords(
|
||||
db,
|
||||
snapshotSha256,
|
||||
updateRecord,
|
||||
verification = null
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(
|
||||
[VENDORGET_STORE_NAMES.gvlSnapshots, VENDORGET_STORE_NAMES.gvlRawEvidence],
|
||||
@@ -665,7 +684,8 @@ function updateGvlRevisionEvidenceRecords(db, snapshotSha256, updateRecord) {
|
||||
snapshotMarked: false,
|
||||
rawEvidenceMarked: false,
|
||||
snapshotSha256,
|
||||
rawGvlSha256: null
|
||||
rawGvlSha256: null,
|
||||
skippedReason: null
|
||||
};
|
||||
|
||||
snapshotRequest.onerror = () => reject(snapshotRequest.error);
|
||||
@@ -673,6 +693,12 @@ function updateGvlRevisionEvidenceRecords(db, snapshotSha256, updateRecord) {
|
||||
const snapshot = snapshotRequest.result ?? null;
|
||||
|
||||
if (!snapshot) {
|
||||
result.skippedReason = "gvl_snapshot_not_found";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!doesGvlRevisionEvidenceMatchVerification(snapshot, verification)) {
|
||||
result.skippedReason = "gvl_revision_evidence_verification_mismatch";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -708,6 +734,40 @@ function updateGvlRevisionEvidenceRecords(db, snapshotSha256, updateRecord) {
|
||||
});
|
||||
}
|
||||
|
||||
function doesGvlRevisionEvidenceMatchVerification(snapshot, verification) {
|
||||
if (!verification) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (verification.valid !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
verification.snapshotSha256 &&
|
||||
snapshot.sha256 !== verification.snapshotSha256
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
verification.vendorListVersion !== null &&
|
||||
verification.vendorListVersion !== undefined &&
|
||||
snapshot.vendorListVersion !== verification.vendorListVersion
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
verification.rawGvlSha256 &&
|
||||
snapshot.rawGvlSha256 !== verification.rawGvlSha256
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function formatGvlEvidenceUtcCompact(date) {
|
||||
return [
|
||||
date.getUTCFullYear(),
|
||||
|
||||
@@ -81,15 +81,6 @@
|
||||
<strong>Analyse-Vorbereitung</strong>
|
||||
<span>Datenbestände und vorbereitete Prüffelder, keine Engine.</span>
|
||||
</a>
|
||||
<a class="workspace-link workspace-placeholder" href="../data-maintenance/data-maintenance.html">
|
||||
<strong>Datenpflege</strong>
|
||||
<span>
|
||||
Gezielte Verwaltung lokaler Datenbestände. Löschen,
|
||||
Wiederherstellen und Exportieren erfolgen künftig segmentbezogen.
|
||||
Vorgesehene Segmente sind GVL-Referenzdaten der Browser-DB,
|
||||
Consent-Daten, Analyse-Daten und weitere künftige Datenbereiche.
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -127,6 +118,16 @@
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section class="panel" aria-labelledby="data-maintenance-title">
|
||||
<h2 id="data-maintenance-title">Datenpflege</h2>
|
||||
<div class="workspace-actions">
|
||||
<a class="workspace-link workspace-placeholder" href="../data-maintenance/data-maintenance.html">
|
||||
<strong>Datenpflege</strong>
|
||||
<span>Verwaltung lokaler Datenbestände.</span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<script src="dashboard.js"></script>
|
||||
|
||||
@@ -113,6 +113,10 @@ p {
|
||||
background: #172033;
|
||||
}
|
||||
|
||||
.protected-revisions {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
button {
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
Consent-Daten, Request-Beobachtungen und Analyse-Daten bleiben
|
||||
unberührt.
|
||||
</p>
|
||||
<button id="purge-gvl-reference-data-button" type="button">
|
||||
<button id="purge-gvl-reference-data-button" type="button" disabled>
|
||||
GVL-Referenzdaten bereinigen
|
||||
</button>
|
||||
</div>
|
||||
@@ -40,7 +40,16 @@
|
||||
class="segment-status"
|
||||
aria-live="polite"
|
||||
>
|
||||
Bereit.
|
||||
–
|
||||
</p>
|
||||
<p
|
||||
id="gvl-reference-protected-revisions"
|
||||
class="protected-revisions"
|
||||
aria-live="polite"
|
||||
hidden
|
||||
></p>
|
||||
<p id="gvl-reference-maintenance-message">
|
||||
Keine GVL-Revisionen.
|
||||
</p>
|
||||
</article>
|
||||
|
||||
|
||||
@@ -1,69 +1,119 @@
|
||||
"use strict";
|
||||
|
||||
const purgeGvlReferenceDataButton = document.getElementById(
|
||||
"purge-gvl-reference-data-button"
|
||||
);
|
||||
const gvlReferenceMaintenanceStatus = document.getElementById(
|
||||
"gvl-reference-maintenance-status"
|
||||
);
|
||||
const gvlReferenceProtectedRevisions = document.getElementById(
|
||||
"gvl-reference-protected-revisions"
|
||||
);
|
||||
const gvlReferenceMaintenanceMessage = document.getElementById(
|
||||
"gvl-reference-maintenance-message"
|
||||
);
|
||||
const purgeGvlReferenceDataButton = document.getElementById(
|
||||
"purge-gvl-reference-data-button"
|
||||
);
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
purgeGvlReferenceDataButton?.addEventListener("click", async () => {
|
||||
await purgeGvlReferenceData();
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
purgeGvlReferenceDataButton.addEventListener("click", async () => {
|
||||
await purgeUnlockedEvidenceRecords();
|
||||
});
|
||||
await renderGvlReferenceMaintenanceStatus();
|
||||
});
|
||||
|
||||
async function purgeGvlReferenceData() {
|
||||
if (!confirm(buildGvlReferenceDataPurgeConfirmationText())) {
|
||||
renderGvlReferenceMaintenanceStatus("Abgebrochen.");
|
||||
async function renderGvlReferenceMaintenanceStatus() {
|
||||
try {
|
||||
const result = await browser.runtime.sendMessage({
|
||||
type: "list_gvl_snapshots"
|
||||
});
|
||||
|
||||
if (!result?.success) {
|
||||
throw new Error(result?.error ?? "list_gvl_snapshots_failed");
|
||||
}
|
||||
|
||||
renderGvlReferenceSnapshotStatus(result.gvlSnapshots ?? []);
|
||||
} catch (error) {
|
||||
renderGvlReferenceStatus("–", true, "Status nicht verfügbar.");
|
||||
renderProtectedRevisions([]);
|
||||
console.warn("VG-Observe GVL maintenance status failed", error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderGvlReferenceSnapshotStatus(snapshots) {
|
||||
if (!snapshots.length) {
|
||||
renderGvlReferenceStatus("–", true, "Keine GVL-Revisionen.");
|
||||
renderProtectedRevisions([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const protectedRevisions = snapshots
|
||||
.filter((snapshot) => snapshot.workspaceDeleteProtected === true)
|
||||
.map((snapshot) => snapshot.vendorListVersion)
|
||||
.filter((vendorListVersion) => vendorListVersion !== null)
|
||||
.filter((vendorListVersion) => vendorListVersion !== undefined);
|
||||
const allRevisionsDeleteAllowed = snapshots.every((snapshot) => {
|
||||
return snapshot.workspaceDeleteAllowed === true;
|
||||
});
|
||||
|
||||
if (allRevisionsDeleteAllowed) {
|
||||
renderGvlReferenceStatus("🔓", false, "GVL-Revisionen löschbar.");
|
||||
} else if (protectedRevisions.length) {
|
||||
renderGvlReferenceStatus("🔒", true, "GVL-Revisionen geschützt.");
|
||||
} else {
|
||||
renderGvlReferenceStatus("–", true, "Status nicht verfügbar.");
|
||||
}
|
||||
|
||||
renderProtectedRevisions(protectedRevisions);
|
||||
}
|
||||
|
||||
function renderGvlReferenceStatus(statusSymbol, buttonDisabled, message) {
|
||||
gvlReferenceMaintenanceStatus.textContent = statusSymbol;
|
||||
purgeGvlReferenceDataButton.disabled = buttonDisabled;
|
||||
gvlReferenceMaintenanceMessage.textContent = message;
|
||||
}
|
||||
|
||||
async function purgeUnlockedEvidenceRecords() {
|
||||
if (
|
||||
!confirm(
|
||||
"Ungesperrte Evidence-Daten mit bestehender Schutzlogik bereinigen?"
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
purgeGvlReferenceDataButton.disabled = true;
|
||||
renderGvlReferenceMaintenanceStatus("GVL-Referenzdaten werden bereinigt...");
|
||||
gvlReferenceMaintenanceMessage.textContent = "Bereinigung läuft...";
|
||||
|
||||
try {
|
||||
const result = await browser.runtime.sendMessage({
|
||||
type: "purge_gvl_reference_data"
|
||||
type: "purge_unlocked_evidence_records"
|
||||
});
|
||||
|
||||
if (!result?.success) {
|
||||
throw new Error(result?.error ?? "purge_gvl_reference_data_failed");
|
||||
throw new Error(result?.error ?? "purge_unlocked_evidence_records_failed");
|
||||
}
|
||||
|
||||
renderGvlReferenceMaintenanceStatus(
|
||||
buildGvlReferenceDataPurgeSuccessMessage(result)
|
||||
);
|
||||
await renderGvlReferenceMaintenanceStatus();
|
||||
gvlReferenceMaintenanceMessage.textContent = buildPurgeSuccessMessage(result);
|
||||
} catch (error) {
|
||||
renderGvlReferenceMaintenanceStatus(
|
||||
"GVL-Referenzdaten konnten nicht bereinigt werden."
|
||||
);
|
||||
console.warn("VG-Observe GVL reference data purge failed", error);
|
||||
} finally {
|
||||
purgeGvlReferenceDataButton.disabled = false;
|
||||
await renderGvlReferenceMaintenanceStatus();
|
||||
gvlReferenceMaintenanceMessage.textContent = "Bereinigung fehlgeschlagen.";
|
||||
console.warn("VG-Observe protected purge failed", error);
|
||||
}
|
||||
}
|
||||
|
||||
function buildGvlReferenceDataPurgeConfirmationText() {
|
||||
return [
|
||||
"GVL-Referenzdaten bereinigen?",
|
||||
"",
|
||||
"Betroffen: GVL-Referenzdaten der Browser-DB.",
|
||||
"Nicht betroffen: Consent-Daten, Request-Beobachtungen, Analyse-Daten.",
|
||||
"",
|
||||
"Diese Aktion entfernt lokale GVL-Referenzdaten aus der Browser-Datenbank."
|
||||
].join("\n");
|
||||
function buildPurgeSuccessMessage(result) {
|
||||
if (Number.isFinite(result.deletedCount)) {
|
||||
return `Bereinigung abgeschlossen: ${result.deletedCount} Records.`;
|
||||
}
|
||||
|
||||
return "Bereinigung abgeschlossen.";
|
||||
}
|
||||
|
||||
function buildGvlReferenceDataPurgeSuccessMessage(result) {
|
||||
const clearedCount = Object.values(result.clearedStores ?? {}).reduce(
|
||||
(total, count) => total + Number(count ?? 0),
|
||||
0
|
||||
);
|
||||
|
||||
return `GVL-Referenzdaten bereinigt: ${clearedCount} Records entfernt.`;
|
||||
}
|
||||
|
||||
function renderGvlReferenceMaintenanceStatus(message) {
|
||||
gvlReferenceMaintenanceStatus.textContent = message;
|
||||
function renderProtectedRevisions(vendorListVersions) {
|
||||
gvlReferenceProtectedRevisions.hidden = vendorListVersions.length === 0;
|
||||
gvlReferenceProtectedRevisions.textContent = vendorListVersions.length
|
||||
? `Geschützt: ${vendorListVersions
|
||||
.map((vendorListVersion) => String(vendorListVersion))
|
||||
.join(", ")}`
|
||||
: "";
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ async function exportSelectedGvlRevisionEvidenceJsonFile() {
|
||||
}
|
||||
|
||||
downloadGvlRevisionEvidenceJsonExport(result.export);
|
||||
await markGvlRevisionEvidenceVaultCopy(result.export);
|
||||
await markGvlRevisionEvidenceVaultCopy(result.export, verification);
|
||||
await renderGvlSnapshots();
|
||||
renderGvlEvidenceTransportStatus(
|
||||
[
|
||||
@@ -271,7 +271,10 @@ function downloadGvlRevisionEvidenceJsonExport(exportContainer) {
|
||||
setTimeout(() => URL.revokeObjectURL(url), 0);
|
||||
}
|
||||
|
||||
async function markGvlRevisionEvidenceVaultCopy(exportContainer) {
|
||||
async function markGvlRevisionEvidenceVaultCopy(
|
||||
exportContainer,
|
||||
verification = null
|
||||
) {
|
||||
const snapshotSha256 = exportContainer?.metadata?.snapshotSha256 ?? null;
|
||||
|
||||
if (!snapshotSha256) {
|
||||
@@ -281,7 +284,8 @@ async function markGvlRevisionEvidenceVaultCopy(exportContainer) {
|
||||
const result = await browser.runtime.sendMessage({
|
||||
type: "mark_gvl_revision_evidence_vault_copy",
|
||||
payload: {
|
||||
snapshotSha256
|
||||
snapshotSha256,
|
||||
verification
|
||||
}
|
||||
});
|
||||
|
||||
@@ -338,6 +342,11 @@ async function verifyGvlRevisionEvidenceJsonFile() {
|
||||
exportContainer
|
||||
);
|
||||
|
||||
if (verification.valid) {
|
||||
await markGvlRevisionEvidenceVaultCopy(exportContainer, verification);
|
||||
await renderGvlSnapshots();
|
||||
}
|
||||
|
||||
renderGvlEvidenceTransportStatus(
|
||||
buildGvlRevisionEvidenceVerificationMessage(verification),
|
||||
verification.valid ? "success" : "error"
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren