Restructure VG-Observe into focused explorer views
Dieser Commit ist enthalten in:
@@ -41,6 +41,13 @@ h2 {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 720px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.dashboard-status {
|
||||
min-height: 18px;
|
||||
font-size: 13px;
|
||||
@@ -55,55 +62,24 @@ h2 {
|
||||
background: #182231;
|
||||
}
|
||||
|
||||
.maintenance-status {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
max-width: 760px;
|
||||
padding: 12px;
|
||||
border: 1px solid #3f6f56;
|
||||
border-radius: 4px;
|
||||
background: #14251d;
|
||||
}
|
||||
|
||||
.maintenance-status strong {
|
||||
font-size: 14px;
|
||||
color: #bbf7d0;
|
||||
}
|
||||
|
||||
.maintenance-status dl {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.maintenance-status dt {
|
||||
margin: 0 0 4px;
|
||||
font-size: 11px;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.maintenance-status dd {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.panel {
|
||||
margin-bottom: 22px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #334155;
|
||||
}
|
||||
|
||||
.metric-grid {
|
||||
.section-help {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.gvl-status-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.metric-grid div {
|
||||
.gvl-status-grid div {
|
||||
min-width: 0;
|
||||
padding: 12px;
|
||||
border: 1px solid #334155;
|
||||
@@ -111,17 +87,18 @@ h2 {
|
||||
background: #1f2937;
|
||||
}
|
||||
|
||||
.metric-grid dt {
|
||||
.gvl-status-grid dt {
|
||||
margin: 0 0 8px;
|
||||
font-size: 12px;
|
||||
line-height: 1.35;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.metric-grid dd {
|
||||
.gvl-status-grid dd {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
table {
|
||||
@@ -150,29 +127,14 @@ th:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 720px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.retention-actions,
|
||||
.admin-actions {
|
||||
.explorer-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.admin-status {
|
||||
min-height: 18px;
|
||||
margin-top: 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.button-link,
|
||||
button {
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #475569;
|
||||
@@ -183,6 +145,12 @@ button {
|
||||
background: #1f2937;
|
||||
}
|
||||
|
||||
.button-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
cursor: default;
|
||||
opacity: 0.65;
|
||||
@@ -193,16 +161,11 @@ button:disabled {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.metric-grid {
|
||||
.gvl-status-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.maintenance-status dl {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.retention-actions,
|
||||
.admin-actions {
|
||||
.explorer-actions {
|
||||
display: grid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,51 +14,11 @@
|
||||
Loading evidence status
|
||||
</div>
|
||||
<p class="dashboard-notice">
|
||||
Verwaltungsbereich: Lesen und manuelle Aktionen bleiben verfügbar.
|
||||
VG-IV dokumentiert browserseitige Consent-/TCF-Zustände als
|
||||
evidenzielle Spiegelung.
|
||||
Übersicht und Einstieg für VG-Observe. Detailansichten liegen in
|
||||
eigenen Explorern.
|
||||
</p>
|
||||
<section class="maintenance-status" aria-label="Verwaltungsmodus">
|
||||
<strong>Verwaltungsmodus aktiv: Hintergrundaufzeichnung ist pausiert.</strong>
|
||||
<dl>
|
||||
<div>
|
||||
<dt>Write Suspend</dt>
|
||||
<dd id="maintenance-write-suspend">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Quelle</dt>
|
||||
<dd id="maintenance-source">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Heartbeat</dt>
|
||||
<dd id="maintenance-heartbeat">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Ablauf</dt>
|
||||
<dd id="maintenance-expires">-</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
</header>
|
||||
|
||||
<section class="panel" aria-labelledby="overview-title">
|
||||
<h2 id="overview-title">Overview</h2>
|
||||
<dl class="metric-grid">
|
||||
<div>
|
||||
<dt>Total Evidence Records</dt>
|
||||
<dd id="total-count">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Locked Records</dt>
|
||||
<dd id="locked-count">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Unlocked Records</dt>
|
||||
<dd id="unlocked-count">-</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section class="panel" aria-labelledby="stores-title">
|
||||
<h2 id="stores-title">Evidence Stores</h2>
|
||||
<table>
|
||||
@@ -93,37 +53,58 @@
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="panel" aria-labelledby="retention-title">
|
||||
<h2 id="retention-title">Retention Status</h2>
|
||||
<p>
|
||||
Locked records are protected from partial purge. Full deletion still
|
||||
requires explicit confirmation.
|
||||
<section class="panel" aria-labelledby="official-gvl-title">
|
||||
<h2 id="official-gvl-title">Offizielle Vendorliste</h2>
|
||||
<p class="section-help">
|
||||
Die aktuell offiziell abgerufene IAB-Europe-Vendorliste ist die
|
||||
Version, die VG-Observe direkt von der offiziellen IAB-Europe-Quelle
|
||||
geladen hat. Sie ist getrennt von der Vendorliste, die in einem
|
||||
konkreten Consent-Kontext gemeldet wurde.
|
||||
</p>
|
||||
<div class="retention-actions">
|
||||
<button id="lock-all-button" type="button">
|
||||
Alle Evidenzen als DSGVO-/DSAR-relevant markieren
|
||||
</button>
|
||||
<button id="unlock-all-button" type="button">
|
||||
Alle Evidenz-Sperren entfernen
|
||||
</button>
|
||||
</div>
|
||||
<dl class="gvl-status-grid">
|
||||
<div>
|
||||
<dt>Lokal neueste gespeicherte Vendorlisten-Version</dt>
|
||||
<dd id="official-gvl-local-version">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Letzter automatischer Update-Check</dt>
|
||||
<dd id="official-gvl-last-check">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Letzter echter automatischer GVL-Check</dt>
|
||||
<dd id="official-gvl-last-real-check">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Nächster erlaubter automatischer GVL-Check</dt>
|
||||
<dd id="official-gvl-next-allowed-check">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Ergebnis des letzten Checks</dt>
|
||||
<dd id="official-gvl-result">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Vorherige Version -> aktuelle Version</dt>
|
||||
<dd id="official-gvl-version-change">-</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section class="panel" aria-labelledby="administration-title">
|
||||
<h2 id="administration-title">Administration</h2>
|
||||
<div class="admin-actions">
|
||||
<button id="gvl-fetch-official-button" type="button">
|
||||
Official IAB GVL Fetch
|
||||
</button>
|
||||
<button id="gvl-import-button" type="button">
|
||||
GVL JSON importieren
|
||||
</button>
|
||||
<button id="evidence-delete-button" type="button">
|
||||
Evidence Delete
|
||||
</button>
|
||||
</div>
|
||||
<div id="admin-status" class="admin-status" aria-live="polite">
|
||||
Bereit
|
||||
<section class="panel" aria-labelledby="explorers-title">
|
||||
<h2 id="explorers-title">Explorer</h2>
|
||||
<p class="section-help">
|
||||
Historische Consent-Zustände und technische Belege werden in einer
|
||||
eigenen Ansicht geöffnet.
|
||||
</p>
|
||||
<div class="explorer-actions">
|
||||
<a class="button-link" href="../consent-explorer/consent-explorer.html">
|
||||
Consent-Explorer öffnen
|
||||
</a>
|
||||
<a class="button-link" href="../gvl-explorer/gvl-explorer.html">
|
||||
GVL-Explorer öffnen
|
||||
</a>
|
||||
<a class="button-link" href="../analysis-dashboard/analysis-dashboard.html">
|
||||
Request-/Empfänger-Analyse öffnen
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
+72
-362
@@ -1,33 +1,20 @@
|
||||
"use strict";
|
||||
|
||||
const EVIDENCE_MAINTENANCE_SOURCE = "dashboard";
|
||||
const EVIDENCE_MAINTENANCE_HEARTBEAT_MS = 5 * 1000;
|
||||
|
||||
const dashboardStatus = document.getElementById("dashboard-status");
|
||||
const totalCount = document.getElementById("total-count");
|
||||
const lockedCount = document.getElementById("locked-count");
|
||||
const unlockedCount = document.getElementById("unlocked-count");
|
||||
const maintenanceWriteSuspend = document.getElementById(
|
||||
"maintenance-write-suspend"
|
||||
const officialGvlLocalVersion = document.getElementById(
|
||||
"official-gvl-local-version"
|
||||
);
|
||||
const maintenanceSource = document.getElementById("maintenance-source");
|
||||
const maintenanceHeartbeat = document.getElementById("maintenance-heartbeat");
|
||||
const maintenanceExpires = document.getElementById("maintenance-expires");
|
||||
const lockAllButton = document.getElementById("lock-all-button");
|
||||
const unlockAllButton = document.getElementById("unlock-all-button");
|
||||
const gvlFetchOfficialButton = document.getElementById(
|
||||
"gvl-fetch-official-button"
|
||||
const officialGvlLastCheck = document.getElementById("official-gvl-last-check");
|
||||
const officialGvlLastRealCheck = document.getElementById(
|
||||
"official-gvl-last-real-check"
|
||||
);
|
||||
const officialGvlNextAllowedCheck = document.getElementById(
|
||||
"official-gvl-next-allowed-check"
|
||||
);
|
||||
const officialGvlResult = document.getElementById("official-gvl-result");
|
||||
const officialGvlVersionChange = document.getElementById(
|
||||
"official-gvl-version-change"
|
||||
);
|
||||
const gvlImportButton = document.getElementById("gvl-import-button");
|
||||
const evidenceDeleteButton = document.getElementById("evidence-delete-button");
|
||||
const adminStatus = document.getElementById("admin-status");
|
||||
const gvlImportFileInput = document.createElement("input");
|
||||
|
||||
gvlImportFileInput.type = "file";
|
||||
gvlImportFileInput.accept = ".json,application/json";
|
||||
gvlImportFileInput.hidden = true;
|
||||
|
||||
let evidenceMaintenanceHeartbeatId = null;
|
||||
|
||||
const storeCells = {
|
||||
consent_states: document.getElementById("store-consent-states"),
|
||||
@@ -38,127 +25,10 @@ const storeCells = {
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
document.body.appendChild(gvlImportFileInput);
|
||||
|
||||
await startEvidenceMaintenanceMode();
|
||||
evidenceMaintenanceHeartbeatId = setInterval(() => {
|
||||
void refreshEvidenceMaintenanceMode();
|
||||
}, EVIDENCE_MAINTENANCE_HEARTBEAT_MS);
|
||||
|
||||
lockAllButton.addEventListener("click", async () => {
|
||||
await handleLockAllClick();
|
||||
});
|
||||
|
||||
unlockAllButton.addEventListener("click", async () => {
|
||||
await handleUnlockAllClick();
|
||||
});
|
||||
|
||||
gvlFetchOfficialButton.addEventListener("click", async () => {
|
||||
await fetchOfficialGvl();
|
||||
});
|
||||
|
||||
gvlImportButton.addEventListener("click", () => {
|
||||
gvlImportFileInput.value = "";
|
||||
gvlImportFileInput.click();
|
||||
});
|
||||
|
||||
gvlImportFileInput.addEventListener("change", async () => {
|
||||
const file = gvlImportFileInput.files?.[0] ?? null;
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
await importGvlFile(file);
|
||||
});
|
||||
|
||||
evidenceDeleteButton.addEventListener("click", async () => {
|
||||
await handleEvidenceDeleteClick();
|
||||
});
|
||||
|
||||
await renderEvidenceStatus();
|
||||
await renderOfficialGvlStatus();
|
||||
});
|
||||
|
||||
window.addEventListener("beforeunload", () => {
|
||||
endEvidenceMaintenanceMode();
|
||||
});
|
||||
|
||||
window.addEventListener("pagehide", () => {
|
||||
endEvidenceMaintenanceMode();
|
||||
});
|
||||
|
||||
async function startEvidenceMaintenanceMode() {
|
||||
try {
|
||||
const status = await sendEvidenceMaintenanceMessage(
|
||||
"start_evidence_maintenance_session"
|
||||
);
|
||||
|
||||
renderEvidenceMaintenanceStatus(status);
|
||||
} catch (error) {
|
||||
renderEvidenceMaintenanceUnavailable();
|
||||
console.warn("VendorGet-IV maintenance start failed", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshEvidenceMaintenanceMode() {
|
||||
try {
|
||||
const status = await sendEvidenceMaintenanceMessage(
|
||||
"refresh_evidence_maintenance_session"
|
||||
);
|
||||
|
||||
renderEvidenceMaintenanceStatus(status);
|
||||
} catch (error) {
|
||||
renderEvidenceMaintenanceUnavailable();
|
||||
console.warn("VendorGet-IV maintenance heartbeat failed", error);
|
||||
}
|
||||
}
|
||||
|
||||
function endEvidenceMaintenanceMode() {
|
||||
if (evidenceMaintenanceHeartbeatId !== null) {
|
||||
clearInterval(evidenceMaintenanceHeartbeatId);
|
||||
evidenceMaintenanceHeartbeatId = null;
|
||||
}
|
||||
|
||||
void sendEvidenceMaintenanceMessage("end_evidence_maintenance_session").catch(
|
||||
(error) => {
|
||||
console.warn("VendorGet-IV maintenance end failed", error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function sendEvidenceMaintenanceMessage(type) {
|
||||
const status = await browser.runtime.sendMessage({
|
||||
type: type,
|
||||
payload: {
|
||||
source: EVIDENCE_MAINTENANCE_SOURCE
|
||||
}
|
||||
});
|
||||
|
||||
if (!status?.success) {
|
||||
throw new Error(status?.error ?? `${type}_failed`);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
function renderEvidenceMaintenanceStatus(status) {
|
||||
maintenanceWriteSuspend.textContent = status.evidenceWriteSuspended
|
||||
? "aktiv"
|
||||
: "inaktiv";
|
||||
maintenanceSource.textContent = status.source ?? "-";
|
||||
maintenanceHeartbeat.textContent = formatMaintenanceTimestamp(
|
||||
status.lastHeartbeatAt
|
||||
);
|
||||
maintenanceExpires.textContent = formatMaintenanceTimestamp(status.expiresAt);
|
||||
}
|
||||
|
||||
function renderEvidenceMaintenanceUnavailable() {
|
||||
maintenanceWriteSuspend.textContent = "unbekannt";
|
||||
maintenanceSource.textContent = "-";
|
||||
maintenanceHeartbeat.textContent = "-";
|
||||
maintenanceExpires.textContent = "-";
|
||||
}
|
||||
|
||||
async function renderEvidenceStatus() {
|
||||
try {
|
||||
const status = await browser.runtime.sendMessage({
|
||||
@@ -169,10 +39,6 @@ async function renderEvidenceStatus() {
|
||||
throw new Error(status?.error ?? "get_evidence_retention_status_failed");
|
||||
}
|
||||
|
||||
totalCount.textContent = String(status.totalCount);
|
||||
lockedCount.textContent = String(status.lockedCount);
|
||||
unlockedCount.textContent = String(status.unlockedCount);
|
||||
|
||||
renderStoreCounts(status.storeCounts ?? {});
|
||||
renderStatusMessage("Evidence status loaded");
|
||||
} catch (error) {
|
||||
@@ -187,250 +53,94 @@ function renderStoreCounts(storeCounts) {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchOfficialGvl() {
|
||||
gvlFetchOfficialButton.disabled = true;
|
||||
renderAdminStatus("Fetching official IAB GVL...");
|
||||
|
||||
async function renderOfficialGvlStatus() {
|
||||
try {
|
||||
const result = await browser.runtime.sendMessage({
|
||||
type: "fetch_official_gvl"
|
||||
type: "get_latest_gvl_update_status"
|
||||
});
|
||||
|
||||
if (!result?.success) {
|
||||
throw new Error(result?.error ?? "official_gvl_fetch_failed");
|
||||
throw new Error(result?.error ?? "get_latest_gvl_update_status_failed");
|
||||
}
|
||||
|
||||
await renderEvidenceStatus();
|
||||
renderAdminStatus(
|
||||
"Fetched successfully - " +
|
||||
`${result.alreadyKnown ? "already known" : "newly stored"} - ` +
|
||||
`vendorListVersion ${result.vendorListVersion ?? "n/a"} - ` +
|
||||
`sha256 ${shortenSha256(result.sha256)}`
|
||||
const status = result.status ?? {};
|
||||
|
||||
officialGvlLocalVersion.textContent = formatNullable(
|
||||
status.latestLocalVendorListVersion ?? status.currentVendorListVersion
|
||||
);
|
||||
officialGvlLastCheck.textContent = formatNullable(status.checkedAt);
|
||||
officialGvlLastRealCheck.textContent = formatNullable(
|
||||
status.lastAutoGvlCheckAt
|
||||
);
|
||||
officialGvlNextAllowedCheck.textContent = formatNullable(
|
||||
status.nextAllowedAutoCheckAt
|
||||
);
|
||||
officialGvlResult.textContent = formatGvlUpdateResult(status);
|
||||
officialGvlVersionChange.textContent = formatGvlVersionChange(status);
|
||||
} catch (error) {
|
||||
renderAdminStatus("Official GVL fetch failed");
|
||||
console.warn("VendorGet-IV official GVL fetch failed", error);
|
||||
} finally {
|
||||
gvlFetchOfficialButton.disabled = false;
|
||||
officialGvlLocalVersion.textContent = "-";
|
||||
officialGvlLastCheck.textContent = "-";
|
||||
officialGvlLastRealCheck.textContent = "-";
|
||||
officialGvlNextAllowedCheck.textContent = "-";
|
||||
officialGvlResult.textContent = "Auto-Check fehlgeschlagen";
|
||||
officialGvlVersionChange.textContent = "-";
|
||||
console.warn("VendorGet-IV official GVL status failed", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function importGvlFile(file) {
|
||||
gvlImportButton.disabled = true;
|
||||
renderAdminStatus("Import läuft...");
|
||||
function formatGvlUpdateResult(status) {
|
||||
const result = status?.result ?? null;
|
||||
|
||||
try {
|
||||
const fileContent = await readFileAsText(file);
|
||||
const rawJson = JSON.parse(fileContent);
|
||||
|
||||
if (!isGvlImportCandidate(rawJson)) {
|
||||
throw new Error("invalid_gvl_json");
|
||||
}
|
||||
|
||||
const result = await browser.runtime.sendMessage({
|
||||
type: "gvl_import_json",
|
||||
payload: {
|
||||
rawJson: rawJson,
|
||||
sourceUrl: "local-file-import"
|
||||
}
|
||||
});
|
||||
|
||||
if (!result?.success) {
|
||||
throw new Error(result?.error ?? "gvl_import_failed");
|
||||
}
|
||||
|
||||
await renderEvidenceStatus();
|
||||
renderAdminStatus(
|
||||
`${result.alreadyKnown ? "already known" : "imported"} - ` +
|
||||
`vendorListVersion ${result.vendorListVersion ?? "n/a"} - ` +
|
||||
`sha256 ${shortenSha256(result.sha256)}`
|
||||
);
|
||||
} catch (error) {
|
||||
renderAdminStatus("Import fehlgeschlagen");
|
||||
console.warn("VendorGet-IV GVL import failed", error);
|
||||
} finally {
|
||||
gvlImportButton.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function readFileAsText(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onerror = () => reject(reader.error);
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
|
||||
function isGvlImportCandidate(value) {
|
||||
return (
|
||||
value &&
|
||||
typeof value === "object" &&
|
||||
!Array.isArray(value) &&
|
||||
value.vendorListVersion !== undefined &&
|
||||
value.vendors &&
|
||||
typeof value.vendors === "object" &&
|
||||
!Array.isArray(value.vendors)
|
||||
);
|
||||
}
|
||||
|
||||
async function handleEvidenceDeleteClick() {
|
||||
evidenceDeleteButton.disabled = true;
|
||||
|
||||
try {
|
||||
const status = await getEvidenceRetentionStatus();
|
||||
|
||||
const confirmed = confirm(
|
||||
"Alle lokal gespeicherten VG-IV-Evidenzdaten wirklich löschen?"
|
||||
);
|
||||
|
||||
if (!confirmed) {
|
||||
renderAdminStatus("Löschung abgebrochen");
|
||||
return;
|
||||
}
|
||||
|
||||
if (status.lockedCount === 0) {
|
||||
await deleteAllEvidenceDatabase();
|
||||
await renderEvidenceStatus();
|
||||
renderAdminStatus("Evidenzdaten gelöscht");
|
||||
return;
|
||||
}
|
||||
|
||||
const deleteLockedRecords = confirm(
|
||||
`Achtung: ${status.lockedCount} Datensätze wurden als ` +
|
||||
"DSGVO-/DSAR-relevant markiert. Sollen auch diese Datensätze " +
|
||||
"wirklich gelöscht werden?"
|
||||
);
|
||||
|
||||
if (deleteLockedRecords) {
|
||||
await deleteAllEvidenceDatabase();
|
||||
await renderEvidenceStatus();
|
||||
renderAdminStatus("Evidenzdaten gelöscht");
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await browser.runtime.sendMessage({
|
||||
type: "purge_unlocked_evidence_records"
|
||||
});
|
||||
|
||||
if (!result?.success) {
|
||||
throw new Error(result?.error ?? "purge_unlocked_evidence_records_failed");
|
||||
}
|
||||
|
||||
await renderEvidenceStatus();
|
||||
renderAdminStatus(
|
||||
`${result.deletedCount} Datensätze gelöscht, ` +
|
||||
`${result.keptLockedCount} gesperrte Datensätze behalten`
|
||||
);
|
||||
} catch (error) {
|
||||
renderAdminStatus("Löschung fehlgeschlagen");
|
||||
console.warn("VendorGet-IV evidence delete failed", error);
|
||||
} finally {
|
||||
evidenceDeleteButton.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function getEvidenceRetentionStatus() {
|
||||
const status = await browser.runtime.sendMessage({
|
||||
type: "get_evidence_retention_status"
|
||||
});
|
||||
|
||||
if (!status?.success) {
|
||||
throw new Error(status?.error ?? "get_evidence_retention_status_failed");
|
||||
if (result === "stored") {
|
||||
return "Neue offizielle Vendorliste gespeichert";
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
async function deleteAllEvidenceDatabase() {
|
||||
const result = await browser.runtime.sendMessage({
|
||||
type: "delete_all_evidence_database"
|
||||
});
|
||||
|
||||
if (!result?.success) {
|
||||
throw new Error(result?.error ?? "delete_all_evidence_database_failed");
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLockAllClick() {
|
||||
const confirmed = confirm(
|
||||
"Alle vorhandenen VG-IV-Evidenzdatensätze als DSGVO-/DSAR-relevant markieren?"
|
||||
);
|
||||
|
||||
if (!confirmed) {
|
||||
renderStatusMessage("Record lock update cancelled");
|
||||
return;
|
||||
if (result === "no_change") {
|
||||
return "Keine neuere offizielle Vendorliste gefunden";
|
||||
}
|
||||
|
||||
await runRecordLockAction({
|
||||
type: "lock_all_evidence_records",
|
||||
payload: {
|
||||
reason: "dsar_used",
|
||||
note: null
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function handleUnlockAllClick() {
|
||||
const confirmed = confirm(
|
||||
"Alle VG-IV-Evidenzsperren wirklich entfernen?"
|
||||
);
|
||||
|
||||
if (!confirmed) {
|
||||
renderStatusMessage("Record lock update cancelled");
|
||||
return;
|
||||
if (result === "already_known") {
|
||||
return "Offizielle Vendorliste war bereits lokal bekannt";
|
||||
}
|
||||
|
||||
await runRecordLockAction({
|
||||
type: "unlock_all_evidence_records"
|
||||
});
|
||||
}
|
||||
|
||||
async function runRecordLockAction(message) {
|
||||
setRecordLockButtonsDisabled(true);
|
||||
|
||||
try {
|
||||
const result = await browser.runtime.sendMessage(message);
|
||||
|
||||
if (!result?.success) {
|
||||
throw new Error(result?.error ?? `${message.type}_failed`);
|
||||
}
|
||||
|
||||
await renderEvidenceStatus();
|
||||
} catch (error) {
|
||||
renderStatusMessage("Record lock update failed");
|
||||
console.warn("VendorGet-IV dashboard record lock update failed", error);
|
||||
} finally {
|
||||
setRecordLockButtonsDisabled(false);
|
||||
if (result === "error") {
|
||||
return "Auto-Check fehlgeschlagen";
|
||||
}
|
||||
|
||||
if (result === "throttled") {
|
||||
return "Übersprungen wegen 24h-Throttling";
|
||||
}
|
||||
|
||||
if (result === "started") {
|
||||
return "Auto-Check läuft";
|
||||
}
|
||||
|
||||
if (result === "not_checked_since_background_start") {
|
||||
return "Noch kein Auto-Check seit Background-Start";
|
||||
}
|
||||
|
||||
return formatNullable(status?.message ?? result);
|
||||
}
|
||||
|
||||
function setRecordLockButtonsDisabled(disabled) {
|
||||
lockAllButton.disabled = disabled;
|
||||
unlockAllButton.disabled = disabled;
|
||||
function formatGvlVersionChange(status) {
|
||||
const previousVersion = formatNullable(status?.previousVendorListVersion);
|
||||
const currentVersion = formatNullable(status?.currentVendorListVersion);
|
||||
|
||||
if (previousVersion === "-" && currentVersion === "-") {
|
||||
return "-";
|
||||
}
|
||||
|
||||
return `${previousVersion} -> ${currentVersion}`;
|
||||
}
|
||||
|
||||
function renderStatusMessage(message) {
|
||||
dashboardStatus.textContent = message;
|
||||
}
|
||||
|
||||
function renderAdminStatus(message) {
|
||||
adminStatus.textContent = message;
|
||||
}
|
||||
|
||||
function shortenSha256(value) {
|
||||
if (!value) {
|
||||
return "n/a";
|
||||
}
|
||||
|
||||
return `${value.slice(0, 12)}...`;
|
||||
}
|
||||
|
||||
function formatMaintenanceTimestamp(value) {
|
||||
if (!value) {
|
||||
function formatNullable(value) {
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return "-";
|
||||
}
|
||||
|
||||
return value;
|
||||
return String(value);
|
||||
}
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren