From 4b533f2d8f1f2d07434989f3e5bcad58b9614451 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Mon, 1 Dec 2025 01:47:59 +0100 Subject: [PATCH] up --- api/v1/result/browser.quick.test.php | 80 +++++++++++++++------------- config/fileload.php | 79 ++++++++++++++++++--------- sql.schema | 20 +++---- 3 files changed, 110 insertions(+), 69 deletions(-) diff --git a/api/v1/result/browser.quick.test.php b/api/v1/result/browser.quick.test.php index 1317e66..bfd3aa6 100644 --- a/api/v1/result/browser.quick.test.php +++ b/api/v1/result/browser.quick.test.php @@ -12,12 +12,19 @@ declare(strict_types=1); */ function browser_quick_test_handle_request(): array { - // Session sicherstellen (sollte über fileload.php schon aktiv sein, - // aber doppelt schadlos) + // Session sicherstellen if (session_status() !== PHP_SESSION_ACTIVE) { session_start(); } + // --------------------------------------------------------------------- + // 0. client_id sicherstellen (persistent browser identifier) + // --------------------------------------------------------------------- + if (empty($_SESSION['client_id'])) { + $_SESSION['client_id'] = bin2hex(random_bytes(32)); // 64 chars + } + $clientId = $_SESSION['client_id']; + // --------------------------------------------------------------------- // 1. JSON einlesen // --------------------------------------------------------------------- @@ -32,21 +39,21 @@ function browser_quick_test_handle_request(): array } // --------------------------------------------------------------------- - // 2. User / Session ermitteln (robust, mehrere Varianten) + // 2. User / Session ermitteln (robust) // --------------------------------------------------------------------- $userId = null; $isLoggedIn = 0; - // A) Klassisch: user_id direkt in der Session + // A) user_id direkt in Session if (!empty($_SESSION['user_id'])) { $userId = (int)$_SESSION['user_id']; } - // B) Dein aktuelles Login verwendet $_SESSION['user']['id'] - elseif (!empty($_SESSION['user']) && is_array($_SESSION['user']) && !empty($_SESSION['user']['id'])) { + // B) dein Login: $_SESSION['user']['id'] + elseif (!empty($_SESSION['user']['id'])) { $userId = (int)$_SESSION['user']['id']; } - // C) Optionaler auth-Block - elseif (!empty($_SESSION['auth']) && is_array($_SESSION['auth']) && !empty($_SESSION['auth']['user_id'])) { + // C) optional auth-Block + elseif (!empty($_SESSION['auth']['user_id'])) { $userId = (int)$_SESSION['auth']['user_id']; } @@ -60,48 +67,44 @@ function browser_quick_test_handle_request(): array $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? null; // --------------------------------------------------------------------- - // 3. Grobe Auswertung aus dem Report (optional) + // 3. Grobe Auswertung aus dem Report // --------------------------------------------------------------------- $modeRequested = $data['mode_requested'] ?? 'unknown'; $meta = $data['meta'] ?? []; - // Browser/OS – vorerst leer, später per Parser füllen $browserName = null; $browserVersion = null; $osName = null; $osVersion = null; - // Gesamtmenge geschriebener/verifizierter Bytes aggregieren + // Byte-Summe aus allen Tests $measuredBytes = 0; - if (!empty($data['quick']) && is_array($data['quick'])) { - $measuredBytes += (int)($data['quick']['size_bytes'] ?? 0); + if (!empty($data['quick']['size_bytes'])) { + $measuredBytes += (int)$data['quick']['size_bytes']; } - if (!empty($data['benchmark']) && is_array($data['benchmark'])) { - $measuredBytes += (int)($data['benchmark']['size_bytes'] ?? 0); + if (!empty($data['benchmark']['size_bytes'])) { + $measuredBytes += (int)$data['benchmark']['size_bytes']; } - if (!empty($data['writeverify']) && is_array($data['writeverify'])) { - $measuredBytes += (int)($data['writeverify']['total_bytes'] ?? 0); + if (!empty($data['writeverify']['total_bytes'])) { + $measuredBytes += (int)$data['writeverify']['total_bytes']; } - // Kapazitätsstatus vorerst neutral $capacityStatus = 'unknown'; - // Volume-/Stick-Daten aktuell noch nicht separat ermittelt + // noch nicht im Browser ermittelt $volumeLabel = null; $manufacturer = null; $modelName = null; $usbType = null; $filesystem = null; - - // advertised_capacity_bytes kennen wir im Browser noch nicht: $advCapacityBytes = null; - // test_report_json = kompletter Report (roher JSON-String) + // kompletter Report $testReportJson = $raw ?: json_encode($data, JSON_UNESCAPED_UNICODE); // --------------------------------------------------------------------- - // 4. Insert in web_quicktests + // 4. Insert // --------------------------------------------------------------------- /** @var PDO $pdo */ global $pdo; @@ -126,7 +129,8 @@ function browser_quick_test_handle_request(): array filesystem, test_report_json, ip_address, - session_id + session_id, + client_id ) VALUES ( :user_id, @@ -146,14 +150,15 @@ function browser_quick_test_handle_request(): array :filesystem, :test_report_json, :ip_address, - :session_id + :session_id, + :client_id ) "; $stmt = $pdo->prepare($sql); $stmt->execute([ - 'user_id' => $userId, // <- hier sollte 1 stehen, wenn eingeloggt + 'user_id' => $userId, 'is_logged_in' => $isLoggedIn, 'usb_device_id' => null, 'browser_name' => $browserName, @@ -171,24 +176,27 @@ function browser_quick_test_handle_request(): array 'test_report_json' => $testReportJson, 'ip_address' => $ipAddress, 'session_id' => $sessionId, + 'client_id' => $clientId ]); $id = (int)$pdo->lastInsertId(); - // DEBUG: damit wir im Frontend/NW-Tab sehen, was der Handler glaubt + // DEBUG-Ausgabe nur für STAGING return [ - 'ok' => true, - 'id' => $id, - 'mode' => $modeRequested, - 'measured_bytes' => $measuredBytes ?: null, + 'ok' => true, + 'id' => $id, + 'mode' => $modeRequested, + 'measured_bytes' => $measuredBytes ?: null, - // Debug-Felder – später für PROD wieder rauswerfen - 'debug_user_id' => $userId, - 'debug_is_logged_in' => $isLoggedIn, - 'debug_session_id' => $sessionId, + // Debug-Info + 'debug_user_id' => $userId, + 'debug_is_logged_in' => $isLoggedIn, + 'debug_session_id' => $sessionId, + 'debug_client_id' => $clientId, 'debug_session_has_user' => isset($_SESSION['user']), - 'debug_session_user' => $_SESSION['user'] ?? null, + 'debug_user' => $_SESSION['user'] ?? null ]; + } catch (Throwable $e) { error_log('[usbcheck] web_quicktests insert failed: ' . $e->getMessage()); diff --git a/config/fileload.php b/config/fileload.php index b14e068..4fc480c 100644 --- a/config/fileload.php +++ b/config/fileload.php @@ -3,38 +3,20 @@ // 0) Umgebung / Domains / Error-Level require_once __DIR__ . "/config.php"; -// ---------------------------------------------------------- -// Session starten (gemeinsam für Frontend + API Subdomains) +// ----------------------------------------------------------- +// Session starten // ----------------------------------------------------------- if (php_sapi_name() !== 'cli') { if (session_status() === PHP_SESSION_NONE) { - // Einheitlicher Name für alle Teilbereiche session_name('usbcheck_session'); - $isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); - - // Cookie-Domain so wählen, dass ALLE Subdomains von usbcheck.it - // dieselbe Session sehen (staging., api.staging., www., …) - $cookieDomain = ''; - if (!empty($_SERVER['HTTP_HOST'])) { - $host = $_SERVER['HTTP_HOST']; - - // alles was auf "usbcheck.it" endet, bekommt die gemeinsame Domain - if (substr($host, -strlen('usbcheck.it')) === 'usbcheck.it') { - // wirkt für usbcheck.it, staging.usbcheck.it, api.staging.usbcheck.it, ... - $cookieDomain = '.usbcheck.it'; - } - } - session_set_cookie_params([ 'lifetime' => 0, 'path' => '/', - // WICHTIG: hier die Domain, damit API + Frontend teilen - 'domain' => $cookieDomain ?: '', - 'secure' => $isHttps, + 'domain' => '', + 'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'), 'httponly' => true, - // eTLD+1 ist gleich (usbcheck.it), daher reicht Lax für Same-Site 'samesite' => 'Lax', ]); @@ -42,7 +24,57 @@ if (php_sapi_name() !== 'cli') { } } -require_once __DIR__ . '/i18n.php'; // <— zentrale Sprachlogik +/** + * --------------------------------------------------------- + * Persistente Client-ID (über Logins & Sessions hinweg) + * --------------------------------------------------------- + * Cookie-Name: usbcheck_client + * Domain: + * - staging: .staging.usbcheck.it + * - live: .usbcheck.it + */ +if (php_sapi_name() !== 'cli') { + $clientId = $_COOKIE['usbcheck_client'] ?? null; + + if (!is_string($clientId) || $clientId === '' || !preg_match('/^[a-f0-9]{32}$/', $clientId)) { + // neue ID erzeugen + try { + $clientId = bin2hex(random_bytes(16)); + } catch (Throwable $e) { + // Fallback – sollte praktisch nie passieren + $clientId = bin2hex(openssl_random_pseudo_bytes(16)); + } + + $host = $_SERVER['HTTP_HOST'] ?? ''; + $cookieDomain = null; + + if (preg_match('/\.staging\.usbcheck\.it$/', $host)) { + $cookieDomain = '.staging.usbcheck.it'; + } elseif (preg_match('/\.usbcheck\.it$/', $host)) { + $cookieDomain = '.usbcheck.it'; + } + + $cookieOpts = [ + 'expires' => time() + 365 * 24 * 60 * 60, + 'path' => '/', + 'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'), + 'httponly' => false, // darf JS lesen, falls du es mal brauchst + 'samesite' => 'Lax', + ]; + + if ($cookieDomain) { + $cookieOpts['domain'] = $cookieDomain; + } + + setcookie('usbcheck_client', $clientId, $cookieOpts); + $_COOKIE['usbcheck_client'] = $clientId; // lokal auch verfügbar + } + + // global verfügbar machen + $GLOBALS['usb_client_id'] = $clientId; +} + +require_once __DIR__ . '/i18n.php'; // <— NEU: zentrale Sprachlogik // ab hier kannst du überall $GLOBALS['lang'] und $GLOBALS['availableLangs'] nutzen // und für JS: @@ -50,7 +82,6 @@ $usbConfig = [ // ... dein sonstiges Zeug ... 'i18n' => app_i18n_get_frontend_config(), ]; - // ----------------------------------------------------------- // 7) Rest des Systems laden // ----------------------------------------------------------- diff --git a/sql.schema b/sql.schema index 1dc2926..fdef831 100644 --- a/sql.schema +++ b/sql.schema @@ -38,7 +38,6 @@ CREATE TABLE IF NOT EXISTS users ( -- ============================================================ -- USB DEVICES – vom Nutzer gespeicherte USB-Sticks --- Ein Benutzer kann mehrere Sticks speichern. -- ============================================================ CREATE TABLE IF NOT EXISTS usb_devices ( @@ -67,7 +66,6 @@ CREATE TABLE IF NOT EXISTS usb_devices ( -- ============================================================ -- USB TEST RESULTS – Schnelltest + Pro-Test --- Jedes Testergebnis gehört zu einem Stick. -- ============================================================ CREATE TABLE IF NOT EXISTS usb_tests ( @@ -97,7 +95,7 @@ CREATE TABLE IF NOT EXISTS usb_tests ( -- Metadaten test_report_json JSON NULL, - ip_address VARCHAR(45) NULL, -- ipv6 kompatibel + ip_address VARCHAR(45) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP @@ -113,7 +111,6 @@ CREATE TABLE IF NOT EXISTS usb_tests ( -- ============================================================ -- WEB QUICKTESTS – Browser-Schnellcheck (Gast + eingeloggt) --- Ergebnisse können anonym oder usergebunden sein. -- ============================================================ CREATE TABLE IF NOT EXISTS web_quicktests ( @@ -132,13 +129,13 @@ CREATE TABLE IF NOT EXISTS web_quicktests ( os_name VARCHAR(100) NULL, os_version VARCHAR(50) NULL, - -- Stick-Infos aus dem Schnelldurchlauf (soweit ermittelbar) - volume_label VARCHAR(255) NULL, -- z.B. "USB DISK", "NO NAME" + -- Stick-Infos + volume_label VARCHAR(255) NULL, manufacturer VARCHAR(255) NULL, model_name VARCHAR(255) NULL, usb_type ENUM('USB 2.0', 'USB 3.0', 'USB 3.1', 'USB 3.2', 'USB 4.0') NULL, - -- Kapazitätsdaten (Hersteller vs. gemessen im Schnellcheck) + -- Kapazität advertised_capacity_bytes BIGINT UNSIGNED NULL, measured_capacity_bytes BIGINT UNSIGNED NULL, @@ -147,13 +144,16 @@ CREATE TABLE IF NOT EXISTS web_quicktests ( filesystem VARCHAR(64) NULL, - -- Detaildaten (z.B. Block-Infos, Logs, Messpunkte) + -- Vollständiger Testreport test_report_json JSON NULL, -- Meta ip_address VARCHAR(45) NULL, session_id VARCHAR(64) NULL, + -- NEU: persistente Browser-/Client-ID zur Wiedererkennung + client_id VARCHAR(64) NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, @@ -162,5 +162,7 @@ CREATE TABLE IF NOT EXISTS web_quicktests ( ON DELETE CASCADE, FOREIGN KEY (usb_device_id) REFERENCES usb_devices(id) - ON DELETE CASCADE + ON DELETE CASCADE, + + INDEX idx_web_quicktests_client_id (client_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;