// /public/assets/js/fakecheck.js document.addEventListener("DOMContentLoaded", () => { const baseUrl = window.fakecheckBaseUrl || ""; const locale = window.fakecheckLocale || "en"; // Neu: API-Basis-URL (prod/staging) – über Config oder Fallback anhand Host const apiBaseUrl = window.fakecheckApiBaseUrl || detectApiBaseUrl(); function detectApiBaseUrl() { const host = window.location.hostname || ""; // einfache Heuristik: staging-Domain → staging-API if (host === "staging.usbcheck.it" || host.endsWith(".staging.usbcheck.it")) { return "https://api.staging.usbcheck.it"; } // default: production return "https://api.usbcheck.it"; } const root = document.getElementById("fc-root"); if (!root) { // Auf anderen Seiten eingebunden? Dann einfach nichts tun. return; } // --- DOM-Helper --------------------------------------------------------- const $ = (sel) => document.querySelector(sel); const $$ = (sel) => Array.from(document.querySelectorAll(sel)); const logEl = $("#fc-log"); const statusTextEl = $("#fc-status-text"); const statusModeEl = $("#fc-status-mode"); const progressInner = $("#fc-progress-inner"); const overallPill = $("#fc-overall-status-pill"); const overallStatus = $("#fc-overall-status-text"); const fsapiWarning = $("#fc-fsapi-warning"); const selectedPathText = $("#fc-selected-path-label"); const saveHint = $("#fc-save-hint"); const saveError = $("#fc-save-error"); const resMode = $("#fc-res-mode"); const resDuration = $("#fc-res-duration"); const resWriteSpeed = $("#fc-res-write-speed"); const resReadSpeed = $("#fc-res-read-speed"); const resWritten = $("#fc-res-written-bytes"); const resVerified = $("#fc-res-verified-bytes"); const btnPickDir = $("#fc-btn-pick-directory"); const btnClearSel = $("#fc-btn-clear-selection"); const btnStart = $("#fc-btn-start-tests"); const btnCancel = $("#fc-btn-cancel-tests"); const modeTiles = $$("#fc-mode-grid .fc-mode-tile"); // Falls du einen Login-Indikator hast, kannst du ihn global setzen, // z. B. window.fakecheckIsLoggedIn = true/false if (window.fakecheckIsLoggedIn) { saveHint.style.display = "block"; } function logLine(message, level = "info") { if (!logEl) return; const line = document.createElement("div"); line.className = "fc-log-line"; const prefix = level === "error" ? "[ERROR] " : level === "warn" ? "[WARN] " : "[INFO] "; line.innerHTML = `${prefix}${message}`; logEl.appendChild(line); logEl.scrollTop = logEl.scrollHeight; } function setStatus(text) { if (statusTextEl) statusTextEl.textContent = text; } function setModeLabel(text) { if (statusModeEl) statusModeEl.textContent = text; } function setProgress(percent) { if (!progressInner) return; const v = Math.min(100, Math.max(0, percent)); progressInner.style.width = v + "%"; } function setOverallStatus(state, text) { if (!overallPill || !overallStatus) return; overallPill.classList.remove("fc-pill-ok", "fc-pill-warn", "fc-pill-bad"); if (state === "ok") overallPill.classList.add("fc-pill-ok"); else if (state === "warn") overallPill.classList.add("fc-pill-warn"); else overallPill.classList.add("fc-pill-bad"); overallStatus.textContent = text; } function formatBytes(bytes) { if (bytes == null) return "–"; const units = ["B", "KB", "MB", "GB", "TB"]; let u = 0; let v = bytes; while (v >= 1024 && u < units.length - 1) { v /= 1024; u++; } return v.toFixed(1) + " " + units[u]; } function formatMbps(bytes, seconds) { if (!seconds || seconds <= 0) return "–"; const bits = bytes * 8; const mbits = bits / (1000 * 1000); return mbits.toFixed(1) + " Mbit/s"; } function formatDuration(seconds) { if (!seconds) return "–"; const s = Math.round(seconds); if (s < 60) return s + " s"; const m = Math.floor(s / 60); const r = s % 60; if (m < 60) return `${m} min ${r}s`; const h = Math.floor(m / 60); const rm = m % 60; return `${h} h ${rm} min`; } // --- Test-Engine (Browser) --------------------------------------------- class UsbBrowserTester { constructor() { this.rootHandle = null; this.abortController = null; } hasFsApiSupport() { return "showDirectoryPicker" in window; } async pickDirectory() { if (!this.hasFsApiSupport()) { throw new Error("File System Access API wird von diesem Browser nicht unterstützt."); } const handle = await window.showDirectoryPicker(); this.rootHandle = handle; return handle; } async clearSelection() { this.rootHandle = null; } async runQuickCheck(report, progressCb, abortSignal) { const TEST_FILENAME = "usbcheck_quick_test.bin"; const TEST_SIZE_MB = 8; const CHUNK_SIZE = 1024 * 1024; const dirHandle = this.rootHandle; if (!dirHandle) throw new Error("Kein Verzeichnis ausgewählt."); logLine("Quick-Check: Vorbereitung...", "info"); const fileHandle = await dirHandle.getFileHandle(TEST_FILENAME, { create: true }); const writable = await fileHandle.createWritable(); const totalBytes = TEST_SIZE_MB * 1024 * 1024; let writtenBytes = 0; const writeStart = performance.now(); while (writtenBytes < totalBytes) { if (abortSignal.aborted) { await writable.abort(); throw new DOMException("Abgebrochen", "AbortError"); } const remaining = totalBytes - writtenBytes; const chunkLen = Math.min(CHUNK_SIZE, remaining); const chunk = new Uint8Array(chunkLen); for (let i = 0; i < chunkLen; i++) { chunk[i] = (i + writtenBytes) % 251; } await writable.write(chunk); writtenBytes += chunkLen; progressCb((writtenBytes / totalBytes) * 100 * 0.5); } await writable.close(); const writeEnd = performance.now(); logLine("Quick-Check: Schreiben abgeschlossen. Verifiziere Daten...", "info"); const file = await fileHandle.getFile(); const readStart = performance.now(); const reader = file.stream().getReader(); let offset = 0; let verifiedBytes = 0; while (true) { if (abortSignal.aborted) { reader.cancel(); throw new DOMException("Abgebrochen", "AbortError"); } const { done, value } = await reader.read(); if (done) break; const chunk = value; for (let i = 0; i < chunk.length; i++) { const expected = (offset + i) % 251; if (chunk[i] !== expected) { logLine(`Quick-Check: Datenfehler bei Byte ${offset + i}`, "error"); throw new Error(`Datenfehler im Quick-Check bei Byte ${offset + i}`); } } offset += chunk.length; verifiedBytes += chunk.length; progressCb(50 + (verifiedBytes / totalBytes) * 100 * 0.5); } const readEnd = performance.now(); const writeSeconds = (writeEnd - writeStart) / 1000; const readSeconds = (readEnd - readStart) / 1000; report.quick = { mode: "quick", test_file: TEST_FILENAME, size_bytes: totalBytes, write_bytes: writtenBytes, read_bytes: verifiedBytes, write_duration_s: writeSeconds, read_duration_s: readSeconds, write_mbit_s: writeSeconds ? (writtenBytes * 8 / (writeSeconds * 1e6)) : null, read_mbit_s: readSeconds ? (verifiedBytes * 8 / (readSeconds * 1e6)) : null, ok: true }; logLine("Quick-Check: Erfolgreich abgeschlossen.", "info"); } async runBenchmark(report, progressCb, abortSignal) { const TEST_FILENAME = "usbcheck_benchmark.bin"; const TEST_SIZE_MB = 32; const CHUNK_SIZE = 1024 * 1024; const dirHandle = this.rootHandle; if (!dirHandle) throw new Error("Kein Verzeichnis ausgewählt."); logLine("Benchmark: Start – schreibe Testdatei...", "info"); const fileHandle = await dirHandle.getFileHandle(TEST_FILENAME, { create: true }); const writable = await fileHandle.createWritable(); const totalBytes = TEST_SIZE_MB * 1024 * 1024; let writtenBytes = 0; const writeStart = performance.now(); while (writtenBytes < totalBytes) { if (abortSignal.aborted) { await writable.abort(); throw new DOMException("Abgebrochen", "AbortError"); } const remaining = totalBytes - writtenBytes; const chunkLen = Math.min(CHUNK_SIZE, remaining); const chunk = new Uint8Array(chunkLen); for (let i = 0; i < chunkLen; i++) { chunk[i] = 0xaa; } await writable.write(chunk); writtenBytes += chunkLen; progressCb((writtenBytes / totalBytes) * 100 * 0.4); } await writable.close(); const writeEnd = performance.now(); logLine("Benchmark: Lesen & Timing...", "info"); const file = await fileHandle.getFile(); const readStart = performance.now(); const reader = file.stream().getReader(); let readBytes = 0; while (true) { if (abortSignal.aborted) { reader.cancel(); throw new DOMException("Abgebrochen", "AbortError"); } const { done, value } = await reader.read(); if (done) break; readBytes += value.length; progressCb(40 + (readBytes / totalBytes) * 100 * 0.6); } const readEnd = performance.now(); const writeSeconds = (writeEnd - writeStart) / 1000; const readSeconds = (readEnd - readStart) / 1000; report.benchmark = { mode: "benchmark", test_file: TEST_FILENAME, size_bytes: totalBytes, write_bytes: writtenBytes, read_bytes: readBytes, write_duration_s: writeSeconds, read_duration_s: readSeconds, write_mbit_s: writeSeconds ? (writtenBytes * 8 / (writeSeconds * 1e6)) : null, read_mbit_s: readSeconds ? (readBytes * 8 / (readSeconds * 1e6)) : null, ok: true }; logLine("Benchmark: Erfolgreich abgeschlossen.", "info"); } async runWriteVerify(report, progressCb, abortSignal) { const BASE_FILENAME = "usbcheck_block_"; const BLOCKS = 4; const BLOCK_SIZE_MB = 32; const CHUNK_SIZE = 1024 * 1024; const dirHandle = this.rootHandle; if (!dirHandle) throw new Error("Kein Verzeichnis ausgewählt."); logLine("Write/Verify: Start – mehrere Blöcke werden getestet...", "info"); const totalBytes = BLOCKS * BLOCK_SIZE_MB * 1024 * 1024; let writtenBytes = 0; let verifiedBytes = 0; const globalStart = performance.now(); const writeDetails = []; const readDetails = []; for (let b = 0; b < BLOCKS; b++) { const filename = `${BASE_FILENAME}${String(b + 1).padStart(2, "0")}.bin`; logLine(`Write/Verify: Block ${b + 1}/${BLOCKS} – ${filename}`, "info"); const fileHandle = await dirHandle.getFileHandle(filename, { create: true }); const writable = await fileHandle.createWritable(); const blockBytes = BLOCK_SIZE_MB * 1024 * 1024; let blockWritten = 0; const blockWriteStart = performance.now(); while (blockWritten < blockBytes) { if (abortSignal.aborted) { await writable.abort(); throw new DOMException("Abgebrochen", "AbortError"); } const remaining = blockBytes - blockWritten; const chunkLen = Math.min(CHUNK_SIZE, remaining); const chunk = new Uint8Array(chunkLen); for (let i = 0; i < chunkLen; i++) { chunk[i] = (i + blockWritten + b * 13) % 251; } await writable.write(chunk); blockWritten += chunkLen; writtenBytes += chunkLen; const progress = (writtenBytes + verifiedBytes) / (totalBytes * 2) * 100; progressCb(progress); } await writable.close(); const blockWriteEnd = performance.now(); writeDetails.push({ block: b + 1, bytes: blockBytes, duration_s: (blockWriteEnd - blockWriteStart) / 1000 }); const file = await fileHandle.getFile(); const reader = file.stream().getReader(); let blockOffset = 0; const blockReadStart = performance.now(); while (true) { if (abortSignal.aborted) { reader.cancel(); throw new DOMException("Abgebrochen", "AbortError"); } const { done, value } = await reader.read(); if (done) break; const chunk = value; for (let i = 0; i < chunk.length; i++) { const expected = (i + blockOffset + b * 13) % 251; if (chunk[i] !== expected) { logLine(`Write/Verify: Datenfehler in Block ${b + 1} bei Byte ${blockOffset + i}`, "error"); throw new Error(`Datenfehler in Block ${b + 1} bei Byte ${blockOffset + i}`); } } blockOffset += chunk.length; verifiedBytes += chunk.length; const progress = (writtenBytes + verifiedBytes) / (totalBytes * 2) * 100; progressCb(progress); } const blockReadEnd = performance.now(); readDetails.push({ block: b + 1, bytes: blockBytes, duration_s: (blockReadEnd - blockReadStart) / 1000 }); } const globalEnd = performance.now(); const totalDuration = (globalEnd - globalStart) / 1000; const sumWrite = writeDetails.reduce((acc, d) => acc + d.bytes, 0); const sumRead = readDetails.reduce((acc, d) => acc + d.bytes, 0); const writeSec = writeDetails.reduce((acc, d) => acc + d.duration_s, 0); const readSec = readDetails.reduce((acc, d) => acc + d.duration_s, 0); report.writeverify = { mode: "writeverify", blocks: BLOCKS, block_size_mb: BLOCK_SIZE_MB, total_bytes: totalBytes, written_bytes: writtenBytes, verified_bytes: verifiedBytes, write_duration_s: writeSec, read_duration_s: readSec, write_mbit_s: writeSec ? (sumWrite * 8 / (writeSec * 1e6)) : null, read_mbit_s: readSec ? (sumRead * 8 / (readSec * 1e6)) : null, ok: true }; report.writeverify_total_duration_s = totalDuration; logLine("Write/Verify: Alle Blöcke erfolgreich verifiziert.", "info"); } async run(mode, updateProgressCb, abortSignal) { if (!this.rootHandle) { throw new Error("Kein USB-Verzeichnis ausgewählt."); } const report = { meta: { base_url: baseUrl, locale: locale, user_agent: navigator.userAgent, started_at: new Date().toISOString() }, tool: "usbcheck_browser", tool_version: "0.1.0", mode_requested: mode, quick: null, benchmark: null, writeverify: null, total_duration_s: null }; const t0 = performance.now(); if (mode === "quick") { await this.runQuickCheck(report, updateProgressCb, abortSignal); } else if (mode === "benchmark") { await this.runBenchmark(report, updateProgressCb, abortSignal); } else if (mode === "writeverify") { await this.runWriteVerify(report, updateProgressCb, abortSignal); } else if (mode === "all") { const modes = ["quick", "benchmark", "writeverify"]; for (let i = 0; i < modes.length; i++) { const subMode = modes[i]; logLine(`All-Inclusive: Starte Teiltest "${subMode}" (${i + 1}/${modes.length})...`, "info"); await this.run( subMode, (p) => { const base = (i / modes.length) * 100; const span = (1 / modes.length) * 100; updateProgressCb(base + (p / 100) * span); }, abortSignal ); } } else { throw new Error("Unbekannter Modus: " + mode); } const t1 = performance.now(); report.total_duration_s = (t1 - t0) / 1000; report.meta.ended_at = new Date().toISOString(); return report; } } const tester = new UsbBrowserTester(); let currentMode = null; let isRunning = false; if (!tester.hasFsApiSupport()) { if (fsapiWarning) fsapiWarning.style.display = "block"; logLine( "Dein Browser unterstützt die File System Access API nicht voll. Einige Funktionen sind deaktiviert.", "warn" ); } function updateStartButtonState() { const hasDir = !!tester.rootHandle; const hasMode = !!currentMode; if (btnStart) btnStart.disabled = !(hasDir && hasMode && !isRunning); if (btnCancel) btnCancel.disabled = !isRunning; } // --- Event-Handler ------------------------------------------------------ if (btnPickDir) { btnPickDir.addEventListener("click", async () => { try { const handle = await tester.pickDirectory(); if (selectedPathText) { selectedPathText.textContent = 'USB-Ordner ausgewählt (Name: "' + (handle.name || "Unbekannt") + '").'; } if (btnClearSel) btnClearSel.disabled = false; setStatus("USB-Verzeichnis ausgewählt. Wähle jetzt einen Testmodus."); logLine("Verzeichnis ausgewählt: " + (handle.name || "[ohne Namen]")); updateStartButtonState(); } catch (err) { if (err.name === "AbortError") { logLine("Verzeichnisauswahl abgebrochen.", "warn"); } else { logLine("Fehler bei Verzeichnisauswahl: " + err.message, "error"); } } }); } if (btnClearSel) { btnClearSel.addEventListener("click", async () => { await tester.clearSelection(); if (selectedPathText) { selectedPathText.textContent = "Noch kein Verzeichnis gewählt."; } btnClearSel.disabled = true; setStatus("Bereit. Wähle zuerst deinen USB-Stick aus."); logLine("Verzeichnisauswahl zurückgesetzt.", "info"); updateStartButtonState(); }); } modeTiles.forEach((tile) => { tile.addEventListener("click", () => { if (tile.classList.contains("disabled")) return; if (isRunning) return; modeTiles.forEach((t) => t.classList.remove("selected")); tile.classList.add("selected"); currentMode = tile.getAttribute("data-mode"); const titleEl = tile.querySelector("h4"); const label = titleEl ? titleEl.textContent : currentMode; setModeLabel(label || ""); setStatus(`Modus "${label}" ausgewählt. Du kannst den Test jetzt starten.`); logLine("Modus gewählt: " + currentMode, "info"); updateStartButtonState(); }); }); if (btnStart) { btnStart.addEventListener("click", async () => { if (!currentMode || !tester.rootHandle || isRunning) return; isRunning = true; updateStartButtonState(); if (btnCancel) btnCancel.disabled = false; if (saveError) saveError.style.display = "none"; setProgress(0); logLine("Starte Tests im Modus: " + currentMode.toUpperCase(), "info"); setStatus("Test läuft... bitte USB-Stick nicht entfernen."); setOverallStatus("warn", "Test läuft..."); const abortController = new AbortController(); tester.abortController = abortController; const updateProgressCb = (percent) => setProgress(percent); let report = null; try { report = await tester.run(currentMode, updateProgressCb, abortController.signal); setProgress(100); setStatus("Test abgeschlossen."); setOverallStatus("ok", "Browser-Test erfolgreich abgeschlossen."); applyReportToDashboard(report); // Debug / Support: console.log("USB Browser Test Report (fakecheck):", report); // Ergebnis speichern: Backend entscheidet, ob der User eingeloggt ist await saveReportToBackend(report); } catch (err) { if (err.name === "AbortError") { setStatus("Test wurde abgebrochen."); setOverallStatus("warn", "Test abgebrochen."); logLine("Test wurde vom Benutzer abgebrochen.", "warn"); } else { setStatus("Fehler: " + err.message); setOverallStatus("bad", "Fehler im Browser-Test."); logLine("Fehler im Test: " + err.message, "error"); } } finally { isRunning = false; tester.abortController = null; updateStartButtonState(); if (btnCancel) btnCancel.disabled = true; } }); } if (btnCancel) { btnCancel.addEventListener("click", () => { if (!isRunning || !tester.abortController) return; tester.abortController.abort(); }); } // --- Dashboard-Füllung -------------------------------------------------- function applyReportToDashboard(report) { const modeLabelMap = { quick: "Quick-Check", benchmark: "Benchmark", writeverify: "Write & Verify", all: "All-Inclusive (alle Browser-Tests)" }; if (resMode) { resMode.textContent = modeLabelMap[report.mode_requested] || report.mode_requested || "–"; } if (resDuration) { resDuration.textContent = formatDuration(report.total_duration_s); } let aggWriteBytes = 0; let aggReadBytes = 0; let writeSec = 0; let readSec = 0; if (report.quick) { aggWriteBytes += report.quick.write_bytes || 0; aggReadBytes += report.quick.read_bytes || 0; writeSec += report.quick.write_duration_s || 0; readSec += report.quick.read_duration_s || 0; } if (report.benchmark) { aggWriteBytes += report.benchmark.write_bytes || 0; aggReadBytes += report.benchmark.read_bytes || 0; writeSec += report.benchmark.write_duration_s || 0; readSec += report.benchmark.read_duration_s || 0; } if (report.writeverify) { aggWriteBytes += report.writeverify.written_bytes || 0; aggReadBytes += report.writeverify.verified_bytes || 0; writeSec += report.writeverify.write_duration_s || 0; readSec += report.writeverify.read_duration_s || 0; } if (resWritten) { resWritten.textContent = aggWriteBytes ? formatBytes(aggWriteBytes) : "–"; } if (resVerified) { resVerified.textContent = aggReadBytes ? formatBytes(aggReadBytes) : "–"; } const avgWriteMbps = (aggWriteBytes && writeSec) ? formatMbps(aggWriteBytes, writeSec) : "–"; const avgReadMbps = (aggReadBytes && readSec) ? formatMbps(aggReadBytes, readSec) : "–"; if (resWriteSpeed) resWriteSpeed.textContent = avgWriteMbps; if (resReadSpeed) resReadSpeed.textContent = avgReadMbps; } // --- Backend-Speicherung ------------------------------------------------ async function saveReportToBackend(report) { // Absoluter Pfad – dein Script liegt oberhalb des public-Roots: const url = "/api/result/browser-quick-test.php"; try { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(report), credentials: "include" }); if (!response.ok) { // 401 => nicht eingeloggt; 500 => Fehler etc. const msg = "HTTP " + response.status; logLine("Backend: Konnte Ergebnis nicht speichern (" + msg + ").", "warn"); if (response.status >= 500 && saveError) { saveError.style.display = "block"; } return; } const data = await response.json().catch(() => null); logLine( "Backend: Testergebnis gespeichert" + (data && data.id ? ` (ID: ${data.id})` : ""), "info" ); } catch (err) { if (saveError) saveError.style.display = "block"; logLine("Fehler beim Speichern im Backend: " + err.message, "error"); } } // --- Seriennummer-Quickcheck (API-gestützt) ----------------------------- function initSerialCheckWidget() { const rootSc = document.getElementById("serialcheck-root"); if (!rootSc) return; // Serialcheck-Partial nicht eingebunden → nichts tun const form = rootSc.querySelector("#serialcheck-form"); const errorBox = rootSc.querySelector("#serialcheck-error"); const resultBox = rootSc.querySelector("#serialcheck-result"); const manufacturerInput = rootSc.querySelector("#sc-manufacturer"); const vidInput = rootSc.querySelector("#sc-vid"); const pidInput = rootSc.querySelector("#sc-pid"); const serialInput = rootSc.querySelector("#sc-serial"); function showScError(msg) { if (!errorBox) return; errorBox.textContent = msg || "Es ist ein Fehler aufgetreten."; errorBox.classList.remove("hidden"); if (resultBox) resultBox.classList.add("hidden"); } function clearScError() { if (!errorBox) return; errorBox.classList.add("hidden"); errorBox.textContent = ""; } function renderScResult(data) { if (!resultBox) return; clearScError(); resultBox.classList.remove("hidden"); const rating = data.rating || "unknown"; let ratingLabel = ""; let ratingDesc = ""; if (rating === "ok") { ratingLabel = "Plausibel"; ratingDesc = "Keine deutlichen Auffälligkeiten erkannt."; } else if (rating === "needs_review") { ratingLabel = "Überprüfen empfohlen"; ratingDesc = "Leichte Auffälligkeiten. In Kombination mit einem technischen Test ergibt sich ein klareres Bild."; } else if (rating === "suspicious") { ratingLabel = "Auffällig / Verdächtig"; ratingDesc = "Deutliche Auffälligkeiten erkannt. Ein Kapazitäts-/Geschwindigkeitstest ist dringend empfohlen."; } else if (rating === "invalid") { ratingLabel = "Ungültig"; ratingDesc = "Die Seriennummer konnte nicht sinnvoll bewertet werden."; } else { ratingLabel = "Unklar"; ratingDesc = "Bewertung nicht eindeutig möglich."; } const input = data.input || {}; const vendorInfo = data.vendor_detected || {}; const serialInfo = data.serial_analysis || {}; const consistency = data.consistency || {}; const issues = serialInfo.issues || []; const notes = consistency.notes || []; const issuesList = issues.length ? '" : 'Keine besonderen Auffälligkeiten.'; const vendorLine = vendorInfo.found ? (vendorInfo.vendor + " (VID " + vendorInfo.vid + ")") : (vendorInfo.vid ? ("Unbekannter Hersteller für VID " + vendorInfo.vid) : "Keine Vendor-ID angegeben"); const notesList = notes.length ? '" : ""; resultBox.innerHTML = `
Bewertung: ${ratingLabel}

${ratingDesc}

Eingabedaten

Hersteller (Angabe):
${input.manufacturer || 'keine Angabe'}
VID / PID:
${(input.vid || "–") + " / " + (input.pid || "–")}
Vendor aus VID:
${vendorLine}

Seriennummer-Analyse

Seriennummer:
${serialInfo.serial || ""}
Länge:
${serialInfo.length || 0} Zeichen
Kategorie:
${serialInfo.category || "-"}
Score:
${typeof serialInfo.score === "number" ? serialInfo.score : "-"} / 100
Auffälligkeiten:
${issuesList}

Hersteller-Konsistenz

${notesList}

Diese Einschätzung basiert auf Heuristiken und kann keine Echtheit garantieren.

`; } if (!form) return; form.addEventListener("submit", (e) => { e.preventDefault(); clearScError(); if (resultBox) resultBox.classList.add("hidden"); const payload = { manufacturer: manufacturerInput ? manufacturerInput.value.trim() : "", vid: vidInput ? vidInput.value.trim() : "", pid: pidInput ? pidInput.value.trim() : "", serial: serialInput ? serialInput.value.trim() : "" }; if (!payload.serial) { showScError("Bitte gib eine Seriennummer ein."); return; } fetch(apiBaseUrl.replace(/\/+$/, "") + "/quickcheck", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }) .then(res => { if (!res.ok) { throw new Error("Server returned status " + res.status); } return res.json(); }) .then(data => { if (!data || !data.success) { throw new Error((data && data.error) || "Unerwartete Antwort vom Server."); } renderScResult(data); }) .catch(err => { showScError("Fehler bei der Prüfung: " + err.message); }); }); } // --- Initialzustand ----------------------------------------------------- setStatus("Bereit. Wähle zuerst deinen USB-Stick aus."); setModeLabel("Kein Modus selektiert"); setOverallStatus("ok", "Noch kein Test durchgeführt."); logLine("USB-Browser-Test (fakecheck) geladen. Warte auf Verzeichnisauswahl und Modus."); updateStartButtonState(); // Serialcheck nur initialisieren, wenn das Partial vorhanden ist initSerialCheckWidget(); });