go
This commit is contained in:
40
api/index.php
Normal file
40
api/index.php
Normal 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
9
api/target/.htaccess
Normal 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
314
api/target/quickcheck.php
Normal 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,
|
||||
];
|
||||
}
|
||||
@@ -21,3 +21,11 @@ if (!defined('APP_URL_FAKECHECK')) {
|
||||
define('MATOMO_URL', 'https://matomo.my-statistics.info/');
|
||||
define('MATOMO_ENABLED', true);
|
||||
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;
|
||||
@@ -21,3 +21,11 @@ if (!defined('APP_URL_FAKECHECK')) {
|
||||
define('MATOMO_URL', 'https://matomo.my-statistics.info/');
|
||||
define('MATOMO_ENABLED', false);
|
||||
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;
|
||||
92
partials/landing/fakecheck/serialcheck.php
Normal file
92
partials/landing/fakecheck/serialcheck.php
Normal 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>
|
||||
@@ -41,6 +41,13 @@ $host = $_SERVER['HTTP_HOST'] ?? '';
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<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">
|
||||
<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
|
||||
// CSS im Header
|
||||
foreach ($GLOBALS['page_styles'] as $style) {
|
||||
|
||||
@@ -4,6 +4,19 @@ 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.
|
||||
@@ -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 -----------------------------------------------------
|
||||
|
||||
setStatus("Bereit. Wähle zuerst deinen USB-Stick aus.");
|
||||
@@ -689,4 +864,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user