This commit is contained in:
2025-11-24 02:12:49 +01:00
parent 71b0a4c72a
commit b2584bc828
8 changed files with 656 additions and 0 deletions

40
api/index.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
// api/index.php
// Optional: zentrale Config laden (wenn du magst)
// require __DIR__ . '/../config/fileload.php';
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
// Pfad aus der URL holen, z.B. /quickcheck?...
$uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
$path = rtrim($uri, '/');
if ($path === '') {
$path = '/';
}
// Routing
switch ($path) {
case '/quickcheck':
require __DIR__ . '/target/quickcheck.php';
$result = quickcheck_handle_request(); // Funktion in quickcheck.php
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
break;
default:
http_response_code(404);
echo json_encode([
'success' => false,
'error' => 'Unknown endpoint',
'path' => $path,
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
break;
}

9
api/target/.htaccess Normal file
View File

@@ -0,0 +1,9 @@
# api/target/.htaccess
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order allow,deny
Deny from all
</IfModule>

314
api/target/quickcheck.php Normal file
View File

@@ -0,0 +1,314 @@
<?php
// api/target/quickcheck.php
/**
* Diese Funktion wird von api/index.php aufgerufen.
* Sie liest die Eingaben, prüft Seriennummer + VID, und gibt ein Array zurück,
* das dann als JSON ausgegeben wird.
*/
function quickcheck_handle_request(): array
{
// JSON Body hat Priorität
$source = quickcheck_get_input();
$vid = isset($source['vid']) ? trim((string)$source['vid']) : '';
$pid = isset($source['pid']) ? trim((string)$source['pid']) : '';
$manufacturer = isset($source['manufacturer']) ? trim((string)$source['manufacturer']) : '';
$serial = isset($source['serial']) ? trim((string)$source['serial']) : '';
if ($serial === '') {
http_response_code(400);
return [
'success' => false,
'error' => 'Missing required parameter: serial',
];
}
// Mini-VID-Datenbank (kannst du später aus Datei/DB laden)
$VID_DB = [
'0781' => 'SanDisk Corp.',
'0951' => 'Kingston Technology',
'054C' => 'Sony Corp.',
'1B1C' => 'Corsair',
'13FE' => 'Phison Electronics',
'8564' => 'Transcend Information, Inc.',
'090C' => 'Silicon Motion, Inc.',
'174C' => 'ASMedia Technology',
'0BC2' => 'Seagate',
// ... nach Bedarf ergänzen
];
$vendorInfo = quickcheck_lookup_vendor($vid, $VID_DB);
$serialAnalysis = quickcheck_analyze_serial($serial);
$consistencyInfo = quickcheck_evaluate_consistency($vendorInfo['vendor'] ?? null, $manufacturer, $serialAnalysis);
// Gesamtrating
$rating = 'ok';
if ($serialAnalysis['category'] === 'invalid') {
$rating = 'invalid';
} elseif ($serialAnalysis['category'] === 'very_suspicious' || $consistencyInfo['manufacturer_match'] === 'mismatch') {
$rating = 'suspicious';
} elseif ($serialAnalysis['category'] === 'suspicious') {
$rating = 'needs_review';
}
return [
'success' => true,
'rating' => $rating,
'input' => [
'vid' => $vid,
'pid' => $pid,
'manufacturer' => $manufacturer,
'serial' => $serial,
],
'vendor_detected' => $vendorInfo,
'serial_analysis' => $serialAnalysis,
'consistency' => $consistencyInfo,
'messages' => [
'de' => [
'Hinweis: Diese Prüfung kann keine Echtheit garantieren, sondern bewertet nur Plausibilität und Auffälligkeiten.',
],
],
];
}
/**
* Eingaben lesen: JSON-Body > POST > GET
*/
function quickcheck_get_input(): array
{
$contentType = $_SERVER['CONTENT_TYPE'] ?? $_SERVER['HTTP_CONTENT_TYPE'] ?? '';
$contentType = strtolower(trim(explode(';', $contentType)[0]));
if ($contentType === 'application/json') {
$raw = file_get_contents('php://input');
if ($raw !== false && $raw !== '') {
$data = json_decode($raw, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($data)) {
return $data;
}
}
}
if (!empty($_POST)) {
return $_POST;
}
if (!empty($_GET)) {
return $_GET;
}
return [];
}
/**
* VID normalisieren + Lookup
*/
function quickcheck_normalize_vid(string $vid): ?string
{
$vid = strtoupper(trim($vid));
$vid = preg_replace('/^0X/i', '', $vid);
if ($vid === '' || !preg_match('/^[0-9A-F]{1,4}$/', $vid)) {
return null;
}
return str_pad($vid, 4, '0', STR_PAD_LEFT);
}
function quickcheck_lookup_vendor(?string $vid, array $VID_DB): array
{
if (!$vid) {
return [
'vid' => null,
'found' => false,
'vendor' => null,
'confidence' => 0,
];
}
$norm = quickcheck_normalize_vid($vid);
if (!$norm) {
return [
'vid' => $vid,
'found' => false,
'vendor' => null,
'confidence' => 0,
'issue' => 'Invalid VID format',
];
}
if (isset($VID_DB[$norm])) {
return [
'vid' => $norm,
'found' => true,
'vendor' => $VID_DB[$norm],
'confidence' => 1.0,
];
}
return [
'vid' => $norm,
'found' => false,
'vendor' => null,
'confidence' => 0.2,
];
}
/**
* Seriennummer grob analysieren (Plausibilität)
*/
function quickcheck_analyze_serial(string $serial): array
{
$serial = trim($serial);
$length = strlen($serial);
$issues = [];
$score = 100;
if ($length === 0) {
return [
'serial' => $serial,
'length' => 0,
'issues' => ['empty'],
'score' => 0,
'category' => 'invalid',
];
}
if ($length < 4) {
$issues[] = 'too_short_critical';
$score -= 60;
} elseif ($length < 8) {
$issues[] = 'short_suspicious';
$score -= 30;
}
if ($length > 32) {
$issues[] = 'very_long';
$score -= 10;
}
if (quickcheck_is_all_same_char($serial)) {
$issues[] = 'all_same_char';
$score -= 50;
}
$simplePatterns = [
'000000', '00000000', '000000000000',
'111111', '123456', '12345678', 'ABCDEF', 'AABBCC'
];
if (in_array(strtoupper($serial), $simplePatterns, true)) {
$issues[] = 'simple_pattern';
$score -= 40;
}
if (quickcheck_is_simple_sequence($serial)) {
$issues[] = 'sequence_pattern';
$score -= 25;
}
$isNumeric = preg_match('/^[0-9]+$/', $serial) === 1;
$isAlpha = preg_match('/^[A-Za-z]+$/', $serial) === 1;
$isHex = preg_match('/^[0-9A-Fa-f]+$/', $serial) === 1;
$isAlnum = preg_match('/^[A-Za-z0-9]+$/', $serial) === 1;
if ($isNumeric) {
$issues[] = 'numeric_only';
$score -= 10;
} elseif ($isAlpha) {
$issues[] = 'letters_only';
$score -= 10;
}
if ($isHex && !$isNumeric && $length >= 8 && $length <= 24) {
$issues[] = 'hex_pattern_plausible';
$score += 5;
}
if ($isAlnum && !$isNumeric && !$isAlpha && $length >= 8) {
$issues[] = 'alnum_mixed_plausible';
$score += 5;
}
if ($score > 100) $score = 100;
if ($score < 0) $score = 0;
if ($length === 0) {
$category = 'invalid';
} elseif ($score >= 80) {
$category = 'plausible';
} elseif ($score >= 50) {
$category = 'suspicious';
} else {
$category = 'very_suspicious';
}
return [
'serial' => $serial,
'length' => $length,
'issues' => array_values(array_unique($issues)),
'score' => $score,
'category' => $category,
];
}
function quickcheck_is_all_same_char(string $s): bool
{
return strlen($s) > 1 && preg_match('/^(.)\1+$/', $s) === 1;
}
function quickcheck_is_simple_sequence(string $s): bool
{
$seqs = [
'123456', '1234567', '12345678', '234567', '345678',
'ABCDEFG', 'ABCDEF', 'ABCDE'
];
$ls = strtoupper($s);
foreach ($seqs as $pattern) {
if (strpos($ls, $ls) !== false && strpos($ls, $pattern) !== false) {
return true;
}
}
return false;
}
/**
* Herstellerangabe vs. Vendor-Name bewerten
*/
function quickcheck_evaluate_consistency(?string $vendorName, string $userManufacturer, array $serialAnalysis): array
{
$vendorName = $vendorName ? trim($vendorName) : '';
$userManufacturer = trim($userManufacturer);
$matchStatus = 'unknown';
$notes = [];
if ($vendorName !== '' && $userManufacturer !== '') {
$v = mb_strtolower($vendorName);
$u = mb_strtolower($userManufacturer);
if (strpos($v, $u) !== false || strpos($u, $v) !== false) {
$matchStatus = 'match';
$notes[] = 'Herstellerangabe passt zur ermittelten Vendor-ID.';
} else {
$matchStatus = 'mismatch';
$notes[] = 'Herstellerangabe weicht von der Vendor-ID ab.';
}
} elseif ($vendorName !== '') {
$notes[] = 'Hersteller über Vendor-ID bekannt, aber keine Herstellerangabe des Nutzers.';
} elseif ($userManufacturer !== '') {
$notes[] = 'Hersteller vom Nutzer angegeben, aber keine oder unbekannte Vendor-ID.';
} else {
$notes[] = 'Keine Herstellerinformationen vorhanden.';
}
$ratingHint = 'neutral';
if ($serialAnalysis['category'] === 'very_suspicious' || $matchStatus === 'mismatch') {
$ratingHint = 'suspicious';
} elseif ($serialAnalysis['category'] === 'plausible' && $matchStatus === 'match') {
$ratingHint = 'good';
}
return [
'manufacturer_match' => $matchStatus,
'notes' => $notes,
'consistency_hint' => $ratingHint,
];
}

View File

@@ -21,3 +21,11 @@ if (!defined('APP_URL_FAKECHECK')) {
define('MATOMO_URL', 'https://matomo.my-statistics.info/'); define('MATOMO_URL', 'https://matomo.my-statistics.info/');
define('MATOMO_ENABLED', true); define('MATOMO_ENABLED', true);
define('MATOMO_SITE_ID', 7); define('MATOMO_SITE_ID', 7);
$env = 'prod';
$baseUrl = 'https://usbcheck.it';
$apiBaseUrl = 'https://api.usbcheck.it';
// Diese Werte später ins Template schieben:
$GLOBALS['usb_env'] = $env;
$GLOBALS['usb_base_url'] = $baseUrl;
$GLOBALS['usb_api_base'] = $apiBaseUrl;

View File

@@ -21,3 +21,11 @@ if (!defined('APP_URL_FAKECHECK')) {
define('MATOMO_URL', 'https://matomo.my-statistics.info/'); define('MATOMO_URL', 'https://matomo.my-statistics.info/');
define('MATOMO_ENABLED', false); define('MATOMO_ENABLED', false);
define('MATOMO_SITE_ID', 8); define('MATOMO_SITE_ID', 8);
$env = 'staging';
$baseUrl = 'https://statging.usbcheck.it';
$apiBaseUrl = 'https://api.staging.usbcheck.it';
// Diese Werte später ins Template schieben:
$GLOBALS['usb_env'] = $env;
$GLOBALS['usb_base_url'] = $baseUrl;
$GLOBALS['usb_api_base'] = $apiBaseUrl;

View File

@@ -0,0 +1,92 @@
<section id="serialcheck-root" class="mx-auto max-w-3xl py-8 px-4">
<div class="mb-4 text-center">
<h2 class="text-2xl font-heading font-bold text-brand-primary mb-2">
Seriennummer prüfen (Quickcheck)
</h2>
<p class="text-sm text-brand-muted">
Gib die Seriennummer deines USB-Sticks ein und wir prüfen die Plausibilität
sowie die Konsistenz deiner Herstellerangabe zu Vendor-ID (falls vorhanden).
</p>
</div>
<div class="bg-white/80 dark:bg-slate-900/70 border border-slate-200/70 dark:border-slate-700 rounded-2xl shadow-sm p-4 mb-4">
<form id="serialcheck-form" class="space-y-4">
<div>
<label for="sc-manufacturer" class="block text-xs font-medium text-slate-700 dark:text-slate-200 mb-1">
Hersteller (laut Aufdruck, optional)
</label>
<input
type="text"
id="sc-manufacturer"
name="manufacturer"
placeholder="z. B. SanDisk, Kingston..."
class="w-full rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-800 px-3 py-2 text-xs focus:outline-none focus:ring-2 focus:ring-brand-primary"
/>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label for="sc-vid" class="block text-xs font-medium text-slate-700 dark:text-slate-200 mb-1">
Vendor-ID (VID, optional)
</label>
<input
type="text"
id="sc-vid"
name="vid"
placeholder="z. B. 0781"
class="w-full rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-800 px-3 py-2 text-xs focus:outline-none focus:ring-2 focus:ring-brand-primary"
/>
<p class="mt-1 text-[10px] text-slate-500">
Hexadezimal, z. B. 0781 (falls bekannt).
</p>
</div>
<div>
<label for="sc-pid" class="block text-xs font-medium text-slate-700 dark:text-slate-200 mb-1">
Product-ID (PID, optional)
</label>
<input
type="text"
id="sc-pid"
name="pid"
placeholder="z. B. 5581"
class="w-full rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-800 px-3 py-2 text-xs focus:outline-none focus:ring-2 focus:ring-brand-primary"
/>
</div>
</div>
<div>
<label for="sc-serial" class="block text-xs font-medium text-slate-700 dark:text-slate-200 mb-1">
Seriennummer (Pflicht)
</label>
<input
type="text"
id="sc-serial"
name="serial"
required
placeholder="Seriennummer genau so eingeben wie angezeigt"
class="w-full rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-800 px-3 py-2 text-xs focus:outline-none focus:ring-2 focus:ring-brand-primary"
/>
</div>
<button
type="submit"
class="inline-flex items-center justify-center rounded-lg bg-brand-primary text-white text-xs font-semibold px-4 py-2 mt-1 hover:bg-brand-primary/90 focus:outline-none focus:ring-2 focus:ring-brand-primary"
>
Seriennummer prüfen
</button>
<p class="mt-2 text-[10px] text-slate-500">
Hinweis: Dieser Quickcheck bewertet nur Plausibilität und Konsistenz und ersetzt keinen
vollständigen Kapazitäts- oder Geschwindigkeitstest.
</p>
</form>
</div>
<div id="serialcheck-error" class="hidden border border-red-300 bg-red-50 text-red-800 rounded-xl px-3 py-2 text-xs mb-3">
<!-- Fehlermeldung -->
</div>
<div id="serialcheck-result" class="hidden bg-white/80 dark:bg-slate-900/70 border border-slate-200/70 dark:border-slate-700 rounded-2xl shadow-sm p-4 text-xs">
<!-- Ergebnis wird per JS eingefügt -->
</div>
</section>

View File

@@ -41,6 +41,13 @@ $host = $_SERVER['HTTP_HOST'] ?? '';
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Montserrat:wght@600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Montserrat:wght@600;700;800&display=swap" rel="stylesheet">
<script>
window.fakecheckBaseUrl = <?= json_encode($GLOBALS['usb_base_url'] ?? '') ?>;
window.fakecheckLocale = <?= json_encode($lang ?? 'en') ?>;
window.fakecheckApiBaseUrl = <?= json_encode($GLOBALS['usb_api_base'] ?? 'https://api.usbcheck.it') ?>;
</script>
<?php <?php
// CSS im Header // CSS im Header
foreach ($GLOBALS['page_styles'] as $style) { foreach ($GLOBALS['page_styles'] as $style) {

View File

@@ -4,6 +4,19 @@ document.addEventListener("DOMContentLoaded", () => {
const baseUrl = window.fakecheckBaseUrl || ""; const baseUrl = window.fakecheckBaseUrl || "";
const locale = window.fakecheckLocale || "en"; 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"); const root = document.getElementById("fc-root");
if (!root) { if (!root) {
// Auf anderen Seiten eingebunden? Dann einfach nichts tun. // Auf anderen Seiten eingebunden? Dann einfach nichts tun.
@@ -682,6 +695,168 @@ document.addEventListener("DOMContentLoaded", () => {
} }
} }
// --- 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
? '<ul class="list-disc list-inside mt-1">' +
issues.map(i => '<li>' + i + '</li>').join("") +
"</ul>"
: '<span class="text-emerald-600 text-[11px]">Keine besonderen Auffälligkeiten.</span>';
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
? '<ul class="list-disc list-inside mt-1 text-[11px]">' +
notes.map(n => "<li>" + n + "</li>").join("") +
"</ul>"
: "";
resultBox.innerHTML = `
<div class="mb-3">
<span class="inline-flex items-center rounded-full px-3 py-1 text-[11px] font-semibold
${rating === "ok" ? "bg-emerald-100 text-emerald-800" : ""}
${rating === "needs_review" ? "bg-amber-100 text-amber-800" : ""}
${rating === "suspicious" ? "bg-red-100 text-red-800" : ""}
${rating === "invalid" ? "bg-slate-100 text-slate-700" : ""}
">
Bewertung: ${ratingLabel}
</span>
<p class="mt-1 text-xs text-slate-600 dark:text-slate-300">${ratingDesc}</p>
</div>
<div class="border border-slate-200 dark:border-slate-700 rounded-xl p-3 mb-3">
<h3 class="text-xs font-semibold mb-1 text-slate-800 dark:text-slate-100">Eingabedaten</h3>
<dl class="text-[11px] space-y-1 text-slate-600 dark:text-slate-300">
<div><dt class="font-medium">Hersteller (Angabe):</dt><dd>${input.manufacturer || '<span class="text-slate-400">keine Angabe</span>'}</dd></div>
<div><dt class="font-medium">VID / PID:</dt><dd>${(input.vid || "") + " / " + (input.pid || "")}</dd></div>
<div><dt class="font-medium">Vendor aus VID:</dt><dd>${vendorLine}</dd></div>
</dl>
</div>
<div class="border border-slate-200 dark:border-slate-700 rounded-xl p-3 mb-3">
<h3 class="text-xs font-semibold mb-1 text-slate-800 dark:text-slate-100">Seriennummer-Analyse</h3>
<dl class="text-[11px] space-y-1 text-slate-600 dark:text-slate-300">
<div><dt class="font-medium">Seriennummer:</dt><dd><code class="text-[10px] bg-slate-100 dark:bg-slate-800 px-1.5 py-0.5 rounded">${serialInfo.serial || ""}</code></dd></div>
<div><dt class="font-medium">Länge:</dt><dd>${serialInfo.length || 0} Zeichen</dd></div>
<div><dt class="font-medium">Kategorie:</dt><dd>${serialInfo.category || "-"}</dd></div>
<div><dt class="font-medium">Score:</dt><dd>${typeof serialInfo.score === "number" ? serialInfo.score : "-"} / 100</dd></div>
<div><dt class="font-medium">Auffälligkeiten:</dt><dd>${issuesList}</dd></div>
</dl>
</div>
<div class="border border-slate-200 dark:border-slate-700 rounded-xl p-3">
<h3 class="text-xs font-semibold mb-1 text-slate-800 dark:text-slate-100">Hersteller-Konsistenz</h3>
${notesList}
<p class="mt-2 text-[10px] text-slate-500">
Diese Einschätzung basiert auf Heuristiken und kann keine Echtheit garantieren.
</p>
</div>
`;
}
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 ----------------------------------------------------- // --- Initialzustand -----------------------------------------------------
setStatus("Bereit. Wähle zuerst deinen USB-Stick aus."); setStatus("Bereit. Wähle zuerst deinen USB-Stick aus.");
@@ -689,4 +864,7 @@ document.addEventListener("DOMContentLoaded", () => {
setOverallStatus("ok", "Noch kein Test durchgeführt."); setOverallStatus("ok", "Noch kein Test durchgeführt.");
logLine("USB-Browser-Test (fakecheck) geladen. Warte auf Verzeichnisauswahl und Modus."); logLine("USB-Browser-Test (fakecheck) geladen. Warte auf Verzeichnisauswahl und Modus.");
updateStartButtonState(); updateStartButtonState();
// Serialcheck nur initialisieren, wenn das Partial vorhanden ist
initSerialCheckWidget();
}); });