Add structured JSON evidence export workflow

Dieser Commit ist enthalten in:
2026-05-27 17:50:44 +02:00
Ursprung 08679f6e00
Commit 31001b9610
5 geänderte Dateien mit 171 neuen und 0 gelöschten Zeilen
+11
Datei anzeigen
@@ -37,6 +37,10 @@ async function handleVendorGetMessage(message, sender) {
return handleFetchOfficialGvlMessage();
}
if (message.type === "export_evidence_json") {
return handleExportEvidenceJsonMessage();
}
if (message.type === "start_evidence_maintenance_session") {
return startEvidenceMaintenanceSession(message?.payload?.source);
}
@@ -169,6 +173,13 @@ async function handleGetEvidenceRetentionStatusMessage() {
};
}
async function handleExportEvidenceJsonMessage() {
return {
success: true,
export: await exportVendorGetEvidenceJson()
};
}
async function handleGetLatestGvlUpdateStatusMessage() {
const db = await openVendorGetDb();
const latestSnapshot = await getLatestGvlSnapshotByVendorListVersion(db);
+80
Datei anzeigen
@@ -0,0 +1,80 @@
"use strict";
async function exportVendorGetEvidenceJson() {
const db = await openVendorGetDb();
const storeNames = [...VENDORGET_EVIDENCE_STORE_NAMES];
const stores = await exportVendorGetEvidenceStores(db, storeNames);
const summary = buildVendorGetEvidenceExportSummary(stores);
return {
metadata: {
exportVersion: 1,
exportedAt: new Date().toISOString(),
project: "VG-Environment",
extension: "VendorGet",
dbName: db.name,
dbVersion: db.version,
stores: storeNames,
summary
},
stores
};
}
function buildVendorGetEvidenceExportSummary(stores) {
const storeRecordCounts = {};
let totalRecordCount = 0;
for (const [storeName, storeExport] of Object.entries(stores)) {
const recordCount = storeExport.recordCount;
storeRecordCounts[storeName] = recordCount;
totalRecordCount += recordCount;
}
return {
totalRecordCount,
storeRecordCounts
};
}
function exportVendorGetEvidenceStores(db, storeNames) {
return new Promise((resolve, reject) => {
const stores = {};
const tx = db.transaction(storeNames, "readonly");
tx.onerror = () => reject(tx.error);
tx.onabort = () => reject(tx.error);
tx.oncomplete = () => resolve(stores);
for (const storeName of storeNames) {
const objectStore = tx.objectStore(storeName);
const storeExport = {
keyPath: objectStore.keyPath,
autoIncrement: objectStore.autoIncrement,
recordCount: 0,
records: []
};
stores[storeName] = storeExport;
const cursorRequest = objectStore.openCursor();
cursorRequest.onerror = () => reject(cursorRequest.error);
cursorRequest.onsuccess = () => {
const cursor = cursorRequest.result;
if (!cursor) {
return;
}
storeExport.records.push({
key: cursor.key,
value: cursor.value
});
storeExport.recordCount += 1;
cursor.continue();
};
}
});
}
+4
Datei anzeigen
@@ -63,6 +63,10 @@
<button id="evidence-dashboard-button" type="button">
Evidence Dashboard öffnen
</button>
<button id="evidence-export-json-button" type="button">
Export Evidence JSON
</button>
<div id="evidence-export-json-status" class="retention-status" aria-live="polite"></div>
<div id="evidence-retention-status" class="retention-status" aria-live="polite">
Status wird geladen
</div>
+75
Datei anzeigen
@@ -13,6 +13,12 @@ const evidenceLockedCount = document.getElementById("evidence-locked-count");
const evidenceDashboardButton = document.getElementById(
"evidence-dashboard-button"
);
const evidenceExportJsonButton = document.getElementById(
"evidence-export-json-button"
);
const evidenceExportJsonStatus = document.getElementById(
"evidence-export-json-status"
);
const evidenceRetentionStatus = document.getElementById(
"evidence-retention-status"
);
@@ -61,6 +67,8 @@ document.addEventListener("DOMContentLoaded", async () => {
url: browser.runtime.getURL("src/dashboard/dashboard.html")
});
});
evidenceExportJsonButton.addEventListener("click", exportEvidenceJsonFile);
});
async function renderSettings() {
@@ -134,3 +142,70 @@ function formatStatusValue(value) {
function renderEvidenceRetentionMessage(message) {
evidenceRetentionStatus.textContent = message;
}
async function exportEvidenceJsonFile() {
evidenceExportJsonButton.disabled = true;
renderEvidenceExportJsonMessage("Export läuft…");
try {
const result = await browser.runtime.sendMessage({
type: "export_evidence_json"
});
if (!result?.success || !result.export) {
throw new Error(result?.error ?? "export_evidence_json_failed");
}
downloadJsonExport(result.export);
renderEvidenceExportJsonMessage(buildExportSuccessMessage(result.export));
} catch (error) {
renderEvidenceExportJsonMessage("Export fehlgeschlagen");
console.warn("VendorGet-IV evidence JSON export failed", error);
} finally {
evidenceExportJsonButton.disabled = false;
}
}
function downloadJsonExport(exportContainer) {
const json = JSON.stringify(exportContainer, null, 2);
const blob = new Blob([json], { type: "application/json" });
const url = URL.createObjectURL(blob);
const downloadLink = document.createElement("a");
downloadLink.href = url;
downloadLink.download = `vendorget-evidence-export-${formatExportTimestamp(
new Date()
)}.json`;
document.body.append(downloadLink);
downloadLink.click();
downloadLink.remove();
setTimeout(() => URL.revokeObjectURL(url), 0);
}
function buildExportSuccessMessage(exportContainer) {
const storeCount = exportContainer?.metadata?.stores?.length ?? 0;
const totalRecordCount =
exportContainer?.metadata?.summary?.totalRecordCount ?? 0;
return `Export gespeichert: ${storeCount} Stores, ${totalRecordCount} Records`;
}
function formatExportTimestamp(date) {
const year = date.getFullYear();
const month = padDatePart(date.getMonth() + 1);
const day = padDatePart(date.getDate());
const hours = padDatePart(date.getHours());
const minutes = padDatePart(date.getMinutes());
const seconds = padDatePart(date.getSeconds());
return `${year}${month}${day}-${hours}${minutes}${seconds}`;
}
function padDatePart(value) {
return String(value).padStart(2, "0");
}
function renderEvidenceExportJsonMessage(message) {
evidenceExportJsonStatus.textContent = message;
}