cve fixes

This commit is contained in:
Jordy Hoogeveen
2026-09-18 13:38:22 +02:00
parent 2420a00777
commit e9192a8aba
11 changed files with 645 additions and 30 deletions
+46 -9
View File
@@ -3,6 +3,7 @@ import JSZip from "jszip";
import { INITIAL_DATA } from "../constants/index.js";
import { STANDAARD_THEMA } from "../constants/themas.js";
import { migrateData, loadFromStorage, saveToStorage } from "../utils/storage.js";
import { bouwHtmlExport, leesHtmlBackup } from "../utils/htmlExport.js";
import { useTheme } from "../hooks/useTheme.js";
const AppContext = createContext(null);
@@ -168,17 +169,43 @@ export function AppProvider({ children }) {
if (saveTimer.current) clearTimeout(saveTimer.current);
};
// ── Backup / restore ──────────────────────────────────────────
// ── Backup / restore / export ─────────────────────────────────
const downloadZip = async (zip, bestandsnaam) => {
const blob = await zip.generateAsync({ type: "blob", compression: "DEFLATE" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = bestandsnaam;
a.click();
URL.revokeObjectURL(url);
};
const doBackup = async () => {
const zip = new JSZip();
const datum = new Date().toISOString().slice(0, 10);
zip.file("data.json", JSON.stringify({ ...data, evVoortgang: evRows, evVoortgangHistory }, null, 2));
zip.file("meta.json", JSON.stringify({ versie: 1, datum, app: "vibefinance", gebruiker: currentUser?.email }, null, 2));
const blob = await zip.generateAsync({ type: "blob", compression: "DEFLATE" });
await downloadZip(zip, `vibefinance_backup_${datum}.zip`);
};
/**
* Export naar één zelfdragende HTML-pagina: leesbaar zonder de app, en met de
* ruwe data ingebakken zodat `doRestore` hem ook weer terug kan zetten.
*/
const doExport = () => {
const datum = new Date().toISOString().slice(0, 10);
const html = bouwHtmlExport({
cats, schuldBlokken, evRows, evVoortgangHistory, schuldHistory,
totEV, totSchuld, doel: ev?.doel || 0,
initieleSchuld: schuld?.initieleSchuld || 0,
gebruiker: currentUser?.email || "",
ruweData: { ...data, evVoortgang: evRows, evVoortgangHistory },
});
const blob = new Blob([html], { type: "text/html;charset=utf-8" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `vibefinance_backup_${datum}.zip`;
a.download = `vibefinance_${datum}.html`;
a.click();
URL.revokeObjectURL(url);
};
@@ -186,15 +213,25 @@ export function AppProvider({ children }) {
const doRestore = () => {
const input = document.createElement("input");
input.type = "file";
input.accept = ".zip";
input.accept = ".zip,.html";
input.onchange = async (e) => {
const file = e.target.files[0];
if (!file) return;
try {
const zip = await JSZip.loadAsync(file);
const entry = zip.file("data.json");
if (!entry) throw new Error("Geen data.json gevonden in backup.");
const parsed = JSON.parse(await entry.async("string"));
// Twee soorten bestanden met dezelfde inhoud: de backup-ZIP met
// `data.json`, en de geëxporteerde HTML-pagina met hetzelfde JSON in
// een script-blok. Zonder dit tweede pad is de belofte onderaan die
// pagina — "hiermee kun je herstellen" — niet waar te maken.
let parsed;
if (file.name.toLowerCase().endsWith(".html")) {
parsed = leesHtmlBackup(await file.text());
if (!parsed) throw new Error("Deze HTML-pagina bevat geen VibeFinance-gegevens.");
} else {
const zip = await JSZip.loadAsync(file);
const entry = zip.file("data.json");
if (!entry) throw new Error("Geen data.json gevonden in backup.");
parsed = JSON.parse(await entry.async("string"));
}
const migrated = migrateData({ eigenVermogen: parsed.eigenVermogen, schuld: parsed.schuld });
const newEvRows = parsed.evVoortgang || [];
const newEvVH = parsed.evVoortgangHistory || [];
@@ -229,7 +266,7 @@ export function AppProvider({ children }) {
evRows, setEvRows,
evVoortgangHistory, setEvVoortgangHistory,
users, setUsers,
doBackup, doRestore,
doBackup, doRestore, doExport,
}}>
{children}
</AppContext.Provider>