Add structured JSON evidence export workflow
Dieser Commit ist enthalten in:
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren