From 9b1f1f0710f36e281f121169967f3dd9f9005881 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Tue, 18 Nov 2025 03:43:21 +0100 Subject: [PATCH] testing --- config/prod/db.php | 28 ++ config/staging/db.php | 28 ++ projektdetails.txt | 91 ++++ public/account.php | 177 ++++++++ public/assets/css/main.css | 555 +++++++++++++++++++++++ public/assets/js/lang.js | 408 +++++++++++++++++ public/index.php | 785 +++++++++------------------------ public/login.php | 82 ++++ public/logout.php | 11 + public/partial/header.php | 75 ++++ public/register.php | 198 +++++++++ public/uploads/avatar/.gitkeep | 0 sql.schema | 111 +++++ src/Auth.php | 284 ++++++++++++ src/Database.php | 25 ++ src/Session.php | 87 ++++ 16 files changed, 2369 insertions(+), 576 deletions(-) create mode 100644 config/prod/db.php create mode 100644 config/staging/db.php create mode 100644 projektdetails.txt create mode 100644 public/account.php create mode 100644 public/assets/css/main.css create mode 100644 public/assets/js/lang.js create mode 100644 public/login.php create mode 100644 public/logout.php create mode 100644 public/partial/header.php create mode 100644 public/register.php create mode 100644 public/uploads/avatar/.gitkeep create mode 100644 sql.schema create mode 100644 src/Auth.php create mode 100644 src/Database.php create mode 100644 src/Session.php diff --git a/config/prod/db.php b/config/prod/db.php new file mode 100644 index 0000000..b19fab1 --- /dev/null +++ b/config/prod/db.php @@ -0,0 +1,28 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false, +]; + +try { + $pdo = new PDO( + "mysql:host={$DB_HOST};dbname={$DB_NAME};charset=utf8mb4", + $DB_USER, + $DB_PASS, + $options + ); +} catch (PDOException $e) { + // In Produktion Logging, keine Details ausgeben + http_response_code(500); + echo 'Database connection error.'; + exit; +} diff --git a/config/staging/db.php b/config/staging/db.php new file mode 100644 index 0000000..b77a411 --- /dev/null +++ b/config/staging/db.php @@ -0,0 +1,28 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false, +]; + +try { + $pdo = new PDO( + "mysql:host={$DB_HOST};dbname={$DB_NAME};charset=utf8mb4", + $DB_USER, + $DB_PASS, + $options + ); +} catch (PDOException $e) { + // In Produktion Logging, keine Details ausgeben + http_response_code(500); + echo 'Database connection error.'; + exit; +} diff --git a/projektdetails.txt b/projektdetails.txt new file mode 100644 index 0000000..760f876 --- /dev/null +++ b/projektdetails.txt @@ -0,0 +1,91 @@ +Projekt Details: +Online Angebot einen USB Speicherstick zu überprüfen. Sprich, stimmt die vom Hersteller angegebene Größe mit der tatsächlichen überein. +Hierzu soll es zwei Varianten geben: +- rein online basiert, die nur Basis checks durchführt +- der Nutzer lädt eine Binary herunter, die es der Website ermöglicht eine detailliertere bzw. tiefergehende Prüfung vorzunehmen. + +Ggf. können die Funktionen später erweitert werden. + +Domains: +- usbcheck.it +- usb-check.it +- ismyusbfake.it +- ismyusbfake.com +- usb-check.com +- fakeusbcheck.com + +Domainaufbau: +Hauptdomain: usbcheck.it (Staging: staging.usbcheck.it) +Domains welche auf diese umgeleitet werden: usb-check.it, usb-check.com + +SocialMedia/LandingPage Domain: ismyusbfake.com (Staging: staging.ismyusbfake.com) +Domains welche auf diese umgeleitet werden: ismyusbfake.it, fakeusbcheck.com + + +Ordnerstruktur usbcheck.it auf Gitlab: +config/ +- prod/ => Nutzung für Configdateien +- staging/ => Nutzung für Configdateien staging. +public/ => hier liegen die generellen Seiten +src/ => Nutzung für generelle Tools (z.B. API) + +Ordnerstruktur usbcheck.it auf Server: +web/config/ => Nutzung für Configdateien +web/public/ => hier zeigt die usbcheck.it hin +web/src/ => Nutzung für generelle Tools (z.B. API) + +Ordnerstruktur staging.usbcheck.it auf Server: +staging/config/ => Nutzung für Configdateien +staging/public/ => hier zeigt die usbcheck.it hin +staging/src/ => Nutzung für generelle Tools (z.B. API) + + +Die Domain ismyusbfake.com zeigt auf einen SubOrdner in web/public/fakecheck und dient als Landingpage, Layout etc. muss von der Hauptdomain übernommen werden +Die Domain staging.ismyusbfake.com zeigt auf einen SubOrdner in staging/public/fakecheck und dient als Landingpage, Layout etc. muss von der Hauptdomain übernommen werden + + +Farbcodes: +{ + "primary": { + "brand_blue": "#0051FF", + "deep_gray": "#1A1A1A", + "silver": "#C8CBD0" + }, + "secondary": { + "green_check": "#03C160", + "error_red": "#E63946", + "amber_yellow": "#FFDA3D" + }, + "neutral": { + "light_gray": "#F4F4F4", + "very_light_gray": "#FAFAFA", + "off_white": "#FFFFFF" + } +} + + +Typografie: +{ + "typography": { + "heading": { + "font_family": "Montserrat", + "font_weight": 700, + "style_name": "Montserrat Bold" + }, + "body": { + "font_family": "Inter", + "font_weight": 400, + "style_name": "Inter Regular" + }, + "subheading": { + "font_family": "Montserrat", + "font_weight": 600, + "style_name": "Montserrat SemiBold" + } + } +} + +Logos: +public/img/logo.png => Logo nur mit Domain +public/img/logo_slogan.png => Logo mit Domain und Slogan +public/img/stick_blank.png => USB Stick ohne Text \ No newline at end of file diff --git a/public/account.php b/public/account.php new file mode 100644 index 0000000..864835f --- /dev/null +++ b/public/account.php @@ -0,0 +1,177 @@ + + + + + + Mein Konto – USBCheck + + + + + + + + + + + + + +
+
+
+

Mein Konto

+

+ Verwalte deine Profildaten und behalte deine USB-Tests im Überblick. +

+ + +
+
+
+ + + + diff --git a/public/assets/css/main.css b/public/assets/css/main.css new file mode 100644 index 0000000..1e133a4 --- /dev/null +++ b/public/assets/css/main.css @@ -0,0 +1,555 @@ +:root { + --brand-blue: #0051FF; + --deep-gray: #1A1A1A; + --silver: #C8CBD0; + --green-check: #03C160; + --error-red: #E63946; + --amber-yellow: #FFDA3D; + --light-gray: #F4F4F4; + --very-light-gray: #FAFAFA; + --off-white: #FFFFFF; + + --radius-lg: 16px; + --radius-pill: 999px; + --shadow-soft: 0 18px 45px rgba(0, 0, 0, 0.08); +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html, body { + margin: 0; + padding: 0; +} + +body { + font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + font-weight: 400; + color: #222; + background: var(--very-light-gray); +} + +/* Layout helpers */ + +.container { + width: 100%; + max-width: 1120px; + margin: 0 auto; + padding: 0 1.5rem; +} + +/* Header */ + +.site-header { + position: sticky; + top: 0; + z-index: 20; + backdrop-filter: blur(10px); + background: rgba(250, 250, 250, 0.92); + border-bottom: 1px solid rgba(200, 203, 208, 0.4); +} + +.header-inner { + display: flex; + align-items: center; + justify-content: space-between; + height: 72px; +} + +.logo-img { + height: 36px; + display: block; +} + +.main-nav { + display: none; + gap: 1.5rem; + margin-left: 2rem; +} + +.nav-link { + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 600; + font-size: 0.95rem; + text-decoration: none; + color: #444; +} + +.nav-link:hover { + color: var(--brand-blue); +} + +.header-actions { + display: flex; + align-items: center; + gap: 1rem; +} + +/* Language switcher */ + +.lang-switch { + position: relative; +} + +.lang-current { + border-radius: var(--radius-pill); + border: 1px solid var(--silver); + background: #fff; + padding: 0.35rem 0.8rem; + font-size: 0.85rem; + cursor: pointer; +} + +.lang-menu { + position: absolute; + right: 0; + top: 120%; + background: #fff; + border-radius: 12px; + box-shadow: var(--shadow-soft); + padding: 0.4rem; + min-width: 140px; +} + +.lang-menu button { + width: 100%; + border: none; + background: transparent; + padding: 0.35rem 0.6rem; + text-align: left; + font-size: 0.85rem; + cursor: pointer; + border-radius: 8px; +} + +.lang-menu button:hover { + background: var(--very-light-gray); +} + +.hidden { + display: none !important; +} + +/* Buttons */ + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: var(--radius-pill); + border: none; + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 600; + font-size: 0.95rem; + padding: 0.6rem 1.4rem; + cursor: pointer; + text-decoration: none; + transition: transform 0.08s ease, box-shadow 0.08s ease, background 0.1s ease; +} + +.btn-primary { + background: linear-gradient(135deg, var(--brand-blue), #2a73ff); + color: #fff; + box-shadow: 0 12px 24px rgba(0, 81, 255, 0.35); +} + +.btn-primary:hover { + transform: translateY(-1px); + box-shadow: 0 16px 32px rgba(0, 81, 255, 0.4); +} + +.btn-outline { + border: 1px solid var(--silver); + background: #fff; + color: #333; +} + +.btn-outline:hover { + background: var(--very-light-gray); +} + +.btn-ghost { + border-radius: var(--radius-pill); + padding-inline: 1.2rem; + background: transparent; + border: 1px solid transparent; + color: #333; +} + +.btn-ghost:hover { + border-color: var(--silver); + background: rgba(0, 0, 0, 0.02); +} + +.btn-disabled { + background: var(--light-gray); + color: #888; + cursor: not-allowed; + box-shadow: none; +} + +/* Avatar */ + +.user-avatar { + width: 36px; + height: 36px; + border-radius: 50%; + background: var(--deep-gray); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 700; + font-size: 0.9rem; +} + +/* Sections */ + +.section { + padding: 4rem 0; +} + +.section-alt { + background: var(--very-light-gray); +} + +.section-title { + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 700; + font-size: 2rem; + margin: 0 0 0.5rem; + color: var(--deep-gray); +} + +.section-lead { + max-width: 680px; + font-size: 1rem; + color: #555; + margin: 0.25rem 0 2.5rem; +} + +/* Hero */ + +.hero { + padding-top: 5rem; + padding-bottom: 4rem; + background: radial-gradient(circle at top left, rgba(0, 81, 255, 0.08), transparent 55%); +} + +.hero-grid { + display: grid; + grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); + gap: 3rem; + align-items: center; +} + +.hero-kicker { + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 600; + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--brand-blue); + margin: 0 0 0.75rem; +} + +.hero-title { + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 700; + font-size: clamp(2rem, 3vw, 2.6rem); + margin: 0 0 0.75rem; + color: var(--deep-gray); +} + +.hero-subtitle { + margin: 0 0 1.5rem; + color: #555; + font-size: 1rem; +} + +.hero-actions { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin-bottom: 1.2rem; +} + +.hero-bullets { + list-style: none; + padding: 0; + margin: 0; + color: #555; + font-size: 0.95rem; +} + +.hero-bullets li::before { + content: "•"; + color: var(--green-check); + font-weight: 700; + display: inline-block; + margin-right: 0.45rem; +} + +.hero-visual { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.hero-card { + background: #fff; + border-radius: 24px; + padding: 1.4rem 1.5rem; + box-shadow: var(--shadow-soft); + border: 1px solid rgba(200, 203, 208, 0.6); +} + +.hero-logo { + width: 180px; + max-width: 100%; + margin-bottom: 0.75rem; +} + +.hero-card-title { + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 600; + margin: 0 0 1rem; + font-size: 1rem; +} + +.hero-metrics { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0.75rem; + margin-bottom: 0.75rem; +} + +.metric-label { + display: block; + font-size: 0.75rem; + color: #777; +} + +.metric-value { + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 700; + font-size: 1rem; +} + +.hero-small { + font-size: 0.75rem; + color: #777; +} + +.hero-stick img { + width: 200px; + max-width: 100%; + filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.25)); +} + +/* Steps / Features / Pricing */ + +.steps-grid, +.features-grid, +.pricing-grid, +.faq-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1.5rem; +} + +.step-card, +.feature-card, +.pricing-card, +.faq-item { + background: #fff; + border-radius: 18px; + padding: 1.3rem 1.4rem; + box-shadow: 0 8px 22px rgba(0, 0, 0, 0.03); + border: 1px solid rgba(230, 230, 230, 0.9); +} + +.section-alt .step-card, +.section-alt .feature-card, +.section-alt .pricing-card, +.section-alt .faq-item { + background: #fff; +} + +.step-icon { + width: 32px; + height: 32px; + border-radius: 12px; + background: rgba(0, 81, 255, 0.07); + color: var(--brand-blue); + display: flex; + align-items: center; + justify-content: center; + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 700; + margin-bottom: 0.8rem; +} + +.step-card h3, +.feature-card h3, +.pricing-card h3 { + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 600; + margin: 0 0 0.5rem; + font-size: 1.1rem; +} + +.step-card p, +.feature-card p, +.pricing-card p, +.faq-item p { + margin: 0; + font-size: 0.95rem; + color: #555; +} + +.feature-card ul, +.pricing-card ul { + list-style: none; + margin: 0.75rem 0 0; + padding: 0; +} + +.feature-card li, +.pricing-card li { + font-size: 0.95rem; + color: #555; + margin-bottom: 0.35rem; +} + +.feature-card li::before, +.pricing-card li::before { + content: "✔"; + color: var(--green-check); + margin-right: 0.45rem; +} + +/* Pro card */ + +.feature-card-pro { + border-color: rgba(0, 81, 255, 0.3); +} + +.pill { + display: inline-block; + padding: 0.18rem 0.55rem; + border-radius: var(--radius-pill); + background: rgba(0, 81, 255, 0.08); + color: var(--brand-blue); + font-size: 0.75rem; + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 600; + margin-bottom: 0.4rem; +} + +/* Pricing */ + +.pricing-card { + position: relative; +} + +.pricing-card-muted { + opacity: 0.85; +} + +.price-tag { + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 700; + font-size: 1.4rem; + margin: 0.3rem 0 0.4rem; +} + +/* FAQ */ + +.faq-item summary { + font-family: 'Montserrat', system-ui, sans-serif; + font-weight: 600; + cursor: pointer; + list-style: none; +} + +.faq-item summary::-webkit-details-marker { + display: none; +} + +.faq-item summary::after { + content: "+"; + float: right; + font-weight: 700; +} + +.faq-item[open] summary::after { + content: "−"; +} + +.faq-item p { + margin-top: 0.6rem; +} + +/* Footer */ + +.site-footer { + border-top: 1px solid rgba(200, 203, 208, 0.6); + background: #fafafa; + padding: 1.5rem 0; + font-size: 0.85rem; + color: #666; +} + +.footer-inner { + display: flex; + align-items: center; + justify-content: space-between; +} + +.footer-links { + display: flex; + gap: 1.2rem; +} + +.footer-links a { + color: #666; + text-decoration: none; +} + +.footer-links a:hover { + text-decoration: underline; +} + +/* Responsive */ + +@media (min-width: 900px) { + .main-nav { + display: flex; + } +} + +@media (max-width: 899px) { + .hero-grid { + grid-template-columns: 1fr; + } + .hero-visual { + order: -1; + } + .header-inner { + gap: 0.75rem; + } +} + +@media (max-width: 720px) { + .steps-grid, + .features-grid, + .pricing-grid, + .faq-grid { + grid-template-columns: 1fr; + } +} diff --git a/public/assets/js/lang.js b/public/assets/js/lang.js new file mode 100644 index 0000000..fbeb4a9 --- /dev/null +++ b/public/assets/js/lang.js @@ -0,0 +1,408 @@ +// public/assets/js/lang.js + +const translations = { + en: { + // Meta / Nav + meta_title: "USBCheck – Test your USB drives", + nav_how_it_works: "How it works", + nav_features: "Features", + nav_pricing: "Pricing", + nav_faq: "FAQ", + btn_login: "Login", + + // Hero + hero_kicker: "USB drive diagnostics", + hero_title: "Check your USB sticks – before you trust them.", + hero_subtitle: "USBCheck helps you evaluate capacity, speed and data integrity of your USB drives – with a quick browser-based test and an optional Pro mode for deeper analysis.", + cta_quick_test: "Start quick test", + cta_how_it_works: "How does it work?", + hero_bullet_1: "Quick test directly in your browser – no account required.", + hero_bullet_2: "Understand real performance, not just what’s printed on the package.", + hero_bullet_3: "Optional advanced tests with a local helper app (coming soon).", + hero_card_title: "Example values from a good USB 3.2 drive", + metric_speed: "Sequential speed", + metric_integrity: "Integrity score", + metric_confidence: "Confidence", + hero_small_hint: "Values are examples – your real USB drive is measured on your own device.", + + // How it works + how_title: "How USBCheck works", + how_intro: "USBCheck combines a browser-based quick test with optional advanced tests via a local helper. You decide how deep you want to go – from a simple speed check to extended capacity and integrity tests.", + how_step1_title: "1. Choose your drive", + how_step1_text: "Start the quick test and select a folder on the USB stick you want to analyse. The browser only sees the folder you explicitly allow.", + how_step2_title: "2. Run the quick test", + how_step2_text: "USBCheck creates temporary test data, measures write & read speed and checks whether everything can be read back correctly.", + how_step3_title: "3. Understand the results", + how_step3_text: "You receive a compact report with speed, integrity and hints on how the result compares to typical values for similar drives.", + how_step4_title: "4. Go deeper (Pro mode, coming soon)", + how_step4_text: "With an optional helper app, advanced tests can check the effective capacity, test larger areas and produce detailed logs – directly controlled from the web interface.", + + // Features + features_title: "Designed for transparency and control", + features_intro: "Whether you just want a quick check or a detailed analysis – USBCheck gives you control over how your USB drive is tested.", + feature_free_title: "Quick test in the browser", + feature_free_1: "No installation – everything runs in your browser.", + feature_free_2: "Select a folder on the USB drive and run a short write/read test.", + feature_free_3: "Get a clear, human-friendly summary of your results.", + feature_pro_title: "Pro mode with helper app", + feature_pro_1: "Extended tests with larger data volumes (planned).", + feature_pro_2: "Optional integration with established tools on your system.", + feature_pro_3: "Detailed reports and export options for documentation.", + label_coming_soon: "Coming soon", + + // Pricing + pricing_title: "Fair model: quick test free, advanced tests optional", + pricing_intro: "The quick browser-based check will remain free. For extended tests and power features, we are planning a transparent Pro model.", + pricing_free_title: "Quick test", + pricing_free_price: "Free", + pricing_free_1: "Browser-based speed and integrity test", + pricing_free_2: "No registration required", + pricing_free_3: "Local test – your data remains on your device", + pricing_free_cta: "Start quick test now", + pricing_pro_title: "Pro mode", + pricing_pro_price: "Planned", + pricing_pro_1: "Extended capacity and surface tests", + pricing_pro_2: "Detailed logs and export options", + pricing_pro_3: "Priority support and more configuration options", + pricing_pro_cta: "Pro mode not yet available", + + // FAQ + faq_title: "Frequently asked questions", + faq_intro: "Here you’ll find answers to common questions about how USBCheck works and how your data is handled.", + faq_q1: "Is my data uploaded to usbcheck.it during the test?", + faq_a1: "No. The quick test runs in your browser and writes temporary data only to the USB drive you have selected. Test data can be deleted afterwards. Only if you actively choose to share anonymous statistics in the future will any information be sent to the server.", + faq_q2: "Can USBCheck detect fake capacity drives?", + faq_a2: "The quick test is designed for speed and integrity. For suspicious drives or capacity checks, the planned Pro mode with helper app will offer deeper tests that can uncover inconsistencies between reported and actual capacity.", + faq_q3: "Which operating systems are supported?", + faq_a3: "The browser-based quick test is available on modern desktop browsers (for example Chromium-based). The Pro mode will support Windows, macOS and Linux via a small helper application.", + faq_q4: "Is frequent testing harmful to my USB drive?", + faq_a4: "Flash memory has a limited number of write cycles. The quick test uses moderate data volumes. For very intensive or repeated tests with large data sizes, we will explicitly point out the potential wear in the Pro mode.", + + // Footer + footer_imprint: "Imprint", + footer_privacy: "Privacy" + }, + + de: { + meta_title: "USBCheck – USB-Sticks testen", + nav_how_it_works: "Funktionsweise", + nav_features: "Funktionen", + nav_pricing: "Preise", + nav_faq: "FAQ", + btn_login: "Login", + + hero_kicker: "USB-Stick-Diagnose", + hero_title: "Prüfe deinen USB-Stick – bevor du ihm vertraust.", + hero_subtitle: "USBCheck hilft dir, Kapazität, Geschwindigkeit und Datenintegrität deiner USB-Sticks einzuschätzen – mit einem schnellen Browser-Test und einem geplanten Pro-Modus für tiefere Analysen.", + cta_quick_test: "Schnelltest starten", + cta_how_it_works: "So funktioniert es", + hero_bullet_1: "Schneller Test direkt im Browser – ohne Registrierung.", + hero_bullet_2: "Verstehe die tatsächliche Leistung, nicht nur die Herstellerangaben.", + hero_bullet_3: "Optional erweiterte Tests mit lokaler Helper-App (in Planung).", + hero_card_title: "Beispielwerte eines guten USB-3.2-Sticks", + metric_speed: "Sequenzielle Geschwindigkeit", + metric_integrity: "Integrität", + metric_confidence: "Einschätzung", + hero_small_hint: "Die gezeigten Werte sind Beispiele – dein eigener USB-Stick wird auf deinem Gerät gemessen.", + + how_title: "So funktioniert USBCheck", + how_intro: "USBCheck kombiniert einen schnellen Browser-Test mit optional erweiterten Prüfungen über eine lokale Helper-App. Du bestimmst, wie tief die Analyse gehen soll.", + how_step1_title: "1. Stick auswählen", + how_step1_text: "Starte den Schnelltest und wähle einen Ordner auf dem USB-Stick, den du prüfen möchtest. Der Browser sieht nur den Ordner, den du freigibst.", + how_step2_title: "2. Schnelltest ausführen", + how_step2_text: "USBCheck schreibt Testdaten, misst Schreib- und Lesegeschwindigkeit und prüft, ob sich alle Daten fehlerfrei zurücklesen lassen.", + how_step3_title: "3. Ergebnisse verstehen", + how_step3_text: "Du erhältst einen kompakten Bericht mit Geschwindigkeit, Integrität und Hinweisen, wie dein Stick im Vergleich zu typischen Werten abschneidet.", + how_step4_title: "4. Tiefere Tests (Pro-Modus, geplant)", + how_step4_text: "Mit einer optionalen Helper-App können größere Bereiche und Kapazität detaillierter geprüft werden – gesteuert über das Web-Interface.", + + features_title: "Transparente Tests für deine USB-Sticks", + features_intro: "Ob schneller Check oder detaillierte Analyse – USBCheck gibt dir Kontrolle darüber, wie dein USB-Stick geprüft wird.", + feature_free_title: "Schnelltest im Browser", + feature_free_1: "Keine Installation – der Test läuft im Browser.", + feature_free_2: "Wähle einen Ordner auf dem Stick und starte einen kurzen Schreib-/Lesetest.", + feature_free_3: "Erhalte eine verständliche Zusammenfassung deiner Ergebnisse.", + feature_pro_title: "Pro-Modus mit Helper-App", + feature_pro_1: "Erweiterte Tests mit größeren Datenmengen (geplant).", + feature_pro_2: "Optionale Anbindung an vorhandene Tools auf deinem System.", + feature_pro_3: "Detaillierte Protokolle und Export-Möglichkeiten.", + label_coming_soon: "Bald verfügbar", + + pricing_title: "Faires Modell: Schnelltest kostenlos", + pricing_intro: "Der Browser-Schnelltest bleibt kostenlos. Für erweiterte Prüfungen planen wir ein transparentes Pro-Modell.", + pricing_free_title: "Schnelltest", + pricing_free_price: "Kostenlos", + pricing_free_1: "Browserbasierter Geschwindigkeits- und Integritätstest", + pricing_free_2: "Keine Registrierung erforderlich", + pricing_free_3: "Lokaler Test – deine Daten bleiben auf deinem Gerät", + pricing_free_cta: "Jetzt Schnelltest starten", + pricing_pro_title: "Pro-Modus", + pricing_pro_price: "In Planung", + pricing_pro_1: "Erweiterte Kapazitäts- und Oberflächentests", + pricing_pro_2: "Detaillierte Logs und Export", + pricing_pro_3: "Priorisierter Support und mehr Optionen", + pricing_pro_cta: "Pro-Modus noch nicht verfügbar", + + faq_title: "Häufige Fragen", + faq_intro: "Hier findest du Antworten auf häufige Fragen zur Funktionsweise von USBCheck und zum Umgang mit deinen Daten.", + faq_q1: "Werden meine Daten während des Tests zu usbcheck.it hochgeladen?", + faq_a1: "Nein. Der Schnelltest läuft im Browser und schreibt temporäre Testdaten nur auf den von dir ausgewählten USB-Stick. Testdaten können anschließend gelöscht werden. Erst wenn du später ausdrücklich zustimmst, können anonyme Statistiken übertragen werden.", + faq_q2: "Kann USBCheck gefälschte Kapazitäten erkennen?", + faq_a2: "Der Schnelltest ist auf Geschwindigkeit und Integrität ausgelegt. Für verdächtige Sticks oder Kapazitätsprüfungen ist im Pro-Modus eine tiefere Analyse geplant, die Unstimmigkeiten zwischen angegebener und tatsächlicher Kapazität aufdecken kann.", + faq_q3: "Welche Betriebssysteme werden unterstützt?", + faq_a3: "Der Browser-Schnelltest funktioniert auf modernen Desktop-Browsern (zum Beispiel Chromium-basiert). Der Pro-Modus wird eine kleine Helper-App für Windows, macOS und Linux nutzen.", + faq_q4: "Schadet häufiges Testen meinem USB-Stick?", + faq_a4: "Flash-Speicher hat nur eine begrenzte Zahl an Schreibzyklen. Der Schnelltest verwendet moderate Datenmengen. Bei sehr intensiven oder wiederholten Tests mit großen Volumina weisen wir im Pro-Modus explizit auf möglichen Verschleiß hin.", + + footer_imprint: "Impressum", + footer_privacy: "Datenschutz" + }, + + it: { + meta_title: "USBCheck – Test dei supporti USB", + nav_how_it_works: "Come funziona", + nav_features: "Funzionalità", + nav_pricing: "Prezzi", + nav_faq: "FAQ", + btn_login: "Login", + + hero_kicker: "Diagnostica delle unità USB", + hero_title: "Controlla le tue chiavette USB prima di fidarti.", + hero_subtitle: "USBCheck ti aiuta a valutare capacità, velocità e integrità dei dati delle tue unità USB – con un rapido test nel browser e una modalità Pro pianificata per analisi più approfondite.", + cta_quick_test: "Avvia test rapido", + cta_how_it_works: "Come funziona", + hero_bullet_1: "Test rapido direttamente nel browser – nessuna registrazione.", + hero_bullet_2: "Comprendi le prestazioni reali, non solo le promesse sulla confezione.", + hero_bullet_3: "Test avanzati con app locale di supporto (in arrivo).", + hero_card_title: "Valori di esempio di una buona unità USB 3.2", + metric_speed: "Velocità sequenziale", + metric_integrity: "Integrità", + metric_confidence: "Valutazione", + hero_small_hint: "I valori mostrati sono di esempio – la tua unità USB viene misurata direttamente sul tuo dispositivo.", + + how_title: "Come funziona USBCheck", + how_intro: "USBCheck combina un test rapido nel browser con test avanzati opzionali tramite un'app locale. Sei tu a decidere quanto approfondita debba essere l'analisi.", + how_step1_title: "1. Seleziona l'unità", + how_step1_text: "Avvia il test rapido e seleziona una cartella sulla chiavetta USB che desideri analizzare. Il browser vede solo la cartella che autorizzi.", + how_step2_title: "2. Esegui il test", + how_step2_text: "USBCheck scrive dati di prova, misura la velocità di scrittura e lettura e controlla che tutto possa essere letto correttamente.", + how_step3_title: "3. Analizza i risultati", + how_step3_text: "Ricevi un report compatto con velocità, integrità e indicazioni su come la tua unità si confronta con valori tipici.", + how_step4_title: "4. Analisi più approfondite (modalità Pro)", + how_step4_text: "Con un'app di supporto opzionale, test estesi possono verificare capacità effettiva e porzioni più grandi dell'unità – gestiti dall'interfaccia web.", + + features_title: "Trasparenza e controllo sui tuoi supporti USB", + features_intro: "Che tu voglia solo un controllo veloce o un’analisi dettagliata, USBCheck ti offre il livello di test adatto alle tue esigenze.", + feature_free_title: "Test rapido nel browser", + feature_free_1: "Nessuna installazione – il test avviene nel browser.", + feature_free_2: "Seleziona una cartella sull’unità USB ed esegui un breve test di scrittura/lettura.", + feature_free_3: "Ottieni un riepilogo chiaro e comprensibile dei risultati.", + feature_pro_title: "Modalità Pro con app locale", + feature_pro_1: "Test estesi con volumi di dati maggiori (in arrivo).", + feature_pro_2: "Integrazione opzionale con strumenti disponibili sul sistema.", + feature_pro_3: "Report dettagliati ed esportazione dei risultati.", + label_coming_soon: "Prossimamente", + + pricing_title: "Modello equo: test rapido gratuito", + pricing_intro: "Il test rapido basato su browser rimarrà gratuito. Per test avanzati stiamo pianificando una modalità Pro trasparente.", + pricing_free_title: "Test rapido", + pricing_free_price: "Gratuito", + pricing_free_1: "Test di velocità e integrità nel browser", + pricing_free_2: "Senza registrazione", + pricing_free_3: "Test locale – i tuoi dati restano sul dispositivo", + pricing_free_cta: "Avvia test rapido", + pricing_pro_title: "Modalità Pro", + pricing_pro_price: "In arrivo", + pricing_pro_1: "Test di capacità e superficie più approfonditi", + pricing_pro_2: "Log dettagliati ed esportazione", + pricing_pro_3: "Supporto prioritario e maggiori opzioni", + pricing_pro_cta: "Modalità Pro non ancora disponibile", + + faq_title: "Domande frequenti", + faq_intro: "Qui trovi le risposte alle domande più comuni su come funziona USBCheck e su come vengono gestiti i tuoi dati.", + faq_q1: "I miei dati vengono caricati su usbcheck.it durante il test?", + faq_a1: "No. Il test rapido viene eseguito nel browser e scrive dati di prova solo sull’unità USB selezionata. I dati di test possono essere eliminati dopo. Solo se in futuro sceglierai esplicitamente di condividere statistiche anonime saranno inviati dati al server.", + faq_q2: "USBCheck può rilevare unità con capacità falsa?", + faq_a2: "Il test rapido è pensato per velocità e integrità. Per unità sospette o controlli di capacità, la modalità Pro offrirà test più approfonditi per individuare discrepanze tra capacità dichiarata e reale.", + faq_q3: "Quali sistemi operativi sono supportati?", + faq_a3: "Il test rapido funziona sui moderni browser desktop (ad esempio basati su Chromium). La modalità Pro utilizzerà una piccola app locale per Windows, macOS e Linux.", + faq_q4: "Test frequenti possono danneggiare la chiavetta USB?", + faq_a4: "La memoria flash ha un numero limitato di cicli di scrittura. Il test rapido utilizza volumi di dati moderati. Per test molto intensivi e ripetuti con grandi quantità di dati, nella modalità Pro segnaleremo chiaramente il possibile usura.", + + footer_imprint: "Note legali", + footer_privacy: "Privacy" + }, + + fr: { + meta_title: "USBCheck – Tester vos clés USB", + nav_how_it_works: "Fonctionnement", + nav_features: "Fonctionnalités", + nav_pricing: "Tarifs", + nav_faq: "FAQ", + btn_login: "Connexion", + + hero_kicker: "Diagnostic de clés USB", + hero_title: "Testez vos clés USB avant de leur confier vos données.", + hero_subtitle: "USBCheck vous aide à évaluer la capacité, la vitesse et l’intégrité des données de vos clés USB – avec un test rapide dans le navigateur et une future version Pro pour des analyses plus poussées.", + cta_quick_test: "Lancer le test rapide", + cta_how_it_works: "Comment ça marche ?", + hero_bullet_1: "Test rapide directement dans le navigateur – sans inscription.", + hero_bullet_2: "Comprenez les performances réelles, pas seulement ce qui est écrit sur l’emballage.", + hero_bullet_3: "Tests avancés avec une application locale (bientôt disponible).", + hero_card_title: "Valeurs d’exemple pour une bonne clé USB 3.2", + metric_speed: "Vitesse séquentielle", + metric_integrity: "Intégrité", + metric_confidence: "Évaluation", + hero_small_hint: "Les valeurs affichées sont des exemples – votre propre clé USB est mesurée directement sur votre appareil.", + + how_title: "Comment fonctionne USBCheck", + how_intro: "USBCheck combine un test rapide dans le navigateur avec des tests avancés optionnels via une application locale. Vous choisissez le niveau d’analyse souhaité.", + how_step1_title: "1. Choisissez la clé", + how_step1_text: "Lancez le test rapide et sélectionnez un dossier sur la clé USB à analyser. Le navigateur n’accède qu’au dossier que vous autorisez.", + how_step2_title: "2. Exécutez le test", + how_step2_text: "USBCheck écrit des données de test, mesure la vitesse d’écriture et de lecture, puis vérifie si tout peut être relu correctement.", + how_step3_title: "3. Interprétez les résultats", + how_step3_text: "Vous obtenez un rapport synthétique avec vitesse, intégrité et des indications pour situer votre clé par rapport à des valeurs typiques.", + how_step4_title: "4. Analyses avancées (mode Pro)", + how_step4_text: "Avec une application locale optionnelle, des tests plus approfondis peuvent vérifier la capacité effective et de plus grandes zones – le tout piloté via l’interface web.", + + features_title: "Transparence et contrôle pour vos clés USB", + features_intro: "Que vous ayez besoin d’un simple contrôle ou d’une analyse détaillée, USBCheck vous laisse décider du niveau de test.", + feature_free_title: "Test rapide dans le navigateur", + feature_free_1: "Aucune installation – tout se fait dans le navigateur.", + feature_free_2: "Sélectionnez un dossier sur la clé USB et lancez un court test d’écriture/lecture.", + feature_free_3: "Recevez un résumé clair et compréhensible des résultats.", + feature_pro_title: "Mode Pro avec application locale", + feature_pro_1: "Tests étendus avec un volume de données plus important (bientôt).", + feature_pro_2: "Intégration optionnelle avec des outils présents sur votre système.", + feature_pro_3: "Rapports détaillés et options d’export.", + label_coming_soon: "Bientôt disponible", + + pricing_title: "Modèle équitable : test rapide gratuit", + pricing_intro: "Le test rapide dans le navigateur restera gratuit. Pour les tests avancés, un mode Pro transparent est prévu.", + pricing_free_title: "Test rapide", + pricing_free_price: "Gratuit", + pricing_free_1: "Test de vitesse et d’intégrité dans le navigateur", + pricing_free_2: "Aucune inscription requise", + pricing_free_3: "Test local – vos données restent sur votre appareil", + pricing_free_cta: "Lancer le test rapide", + pricing_pro_title: "Mode Pro", + pricing_pro_price: "À venir", + pricing_pro_1: "Tests de capacité et de surface plus approfondis", + pricing_pro_2: "Journalisation détaillée et export", + pricing_pro_3: "Support prioritaire et réglages avancés", + pricing_pro_cta: "Mode Pro pas encore disponible", + + faq_title: "Questions fréquentes", + faq_intro: "Voici des réponses aux questions les plus fréquentes sur le fonctionnement d’USBCheck et la gestion de vos données.", + faq_q1: "Mes données sont-elles envoyées à usbcheck.it pendant le test ?", + faq_a1: "Non. Le test rapide s’exécute dans le navigateur et écrit des données de test uniquement sur la clé USB que vous avez choisie. Ces données peuvent être supprimées ensuite. Seules des statistiques anonymes pourront être envoyées si vous l’acceptez explicitement à l’avenir.", + faq_q2: "USBCheck peut-il détecter une capacité falsifiée ?", + faq_a2: "Le test rapide est axé sur la vitesse et l’intégrité. Pour les clés suspectes ou les vérifications de capacité, le mode Pro offrira des tests plus approfondis afin de détecter des incohérences entre capacité annoncée et réelle.", + faq_q3: "Quels systèmes d’exploitation sont supportés ?", + faq_a3: "Le test rapide fonctionne sur les navigateurs modernes de bureau (par exemple basés sur Chromium). Le mode Pro utilisera une petite application locale pour Windows, macOS et Linux.", + faq_q4: "Des tests fréquents peuvent-ils endommager ma clé USB ?", + faq_a4: "La mémoire flash dispose d’un nombre limité de cycles d’écriture. Le test rapide utilise un volume de données modéré. Pour des tests très intensifs et répétés avec beaucoup de données, nous signalerons explicitement ce risque dans le mode Pro.", + + footer_imprint: "Mentions légales", + footer_privacy: "Confidentialité" + } +}; + +// Helper to get lang from URL +function getLangFromUrl() { + const params = new URLSearchParams(window.location.search); + const l = (params.get('lang') || "").toLowerCase(); + if (["de", "en", "it", "fr"].includes(l)) return l; + return null; +} + +// Helper to detect from browser +function detectBrowserLang() { + const navLang = (navigator.language || navigator.userLanguage || "en").toLowerCase(); + if (navLang.startsWith("de")) return "de"; + if (navLang.startsWith("it")) return "it"; + if (navLang.startsWith("fr")) return "fr"; + return "en"; +} + +function applyLang(lang) { + const dict = translations[lang] || translations.en; + + // Update and + document.documentElement.lang = lang; + if (dict.meta_title) { + document.title = dict.meta_title; + } + + // Update text nodes + document.querySelectorAll("[data-i18n]").forEach(el => { + const key = el.getAttribute("data-i18n"); + const text = dict[key]; + if (typeof text === "string") { + el.textContent = text; + } + }); + + // Language switcher label + const currentBtn = document.getElementById("lang-current"); + if (currentBtn) { + currentBtn.textContent = lang.toUpperCase(); + } + + // Update quick-test link to keep lang parameter if present + const quickBtn = document.getElementById("quick-test-btn"); + if (quickBtn) { + const base = "/fakecheck/"; + // optional: append ?lang=xx for deep links + quickBtn.href = base + "?lang=" + lang; + } +} + +function initLangSwitcher(currentLang) { + const trigger = document.getElementById("lang-current"); + const menu = document.getElementById("lang-menu"); + if (!trigger || !menu) return; + + trigger.addEventListener("click", () => { + menu.classList.toggle("hidden"); + }); + + menu.querySelectorAll("button[data-lang]").forEach(btn => { + btn.addEventListener("click", () => { + const newLang = btn.getAttribute("data-lang"); + const url = new URL(window.location.href); + url.searchParams.set("lang", newLang); + window.location.href = url.toString(); + }); + }); + + document.addEventListener("click", (e) => { + if (!menu.contains(e.target) && e.target !== trigger) { + menu.classList.add("hidden"); + } + }); +} + +function initLoginAvatar() { + const loginBtn = document.getElementById("login-button"); + const avatar = document.getElementById("user-avatar"); + if (!loginBtn || !avatar) return; + + // Demo: toggle avatar on click; später durch echte Session-Logik ersetzen + loginBtn.addEventListener("click", () => { + // Hier würdest du redirect zum echten Login einbauen. + // Für das Layout-Demo simulieren wir einfach "eingeloggt". + loginBtn.classList.add("hidden"); + avatar.classList.remove("hidden"); + // Avatar-Initialen könnten später vom Usernamen kommen. + }); +} + +document.addEventListener("DOMContentLoaded", () => { + const urlLang = getLangFromUrl(); + const lang = urlLang || detectBrowserLang(); + applyLang(lang); + initLangSwitcher(lang); + initLoginAvatar(); +}); diff --git a/public/index.php b/public/index.php index 70bf4fd..e52dd90 100644 --- a/public/index.php +++ b/public/index.php @@ -1,601 +1,234 @@ +<?php +// public/index.php + +// Einfache Sprach-Erkennung +$supportedLangs = ['de', 'en', 'it', 'fr']; +$defaultLang = 'en'; + +// 1) Direkt per ?lang=de +if (isset($_GET['lang']) && in_array($_GET['lang'], $supportedLangs, true)) { + $currentLang = $_GET['lang']; +} else { + // 2) Grobe Erkennung aus Accept-Language + $currentLang = $defaultLang; + if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + $accepted = strtolower(substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2)); + if (in_array($accepted, $supportedLangs, true)) { + $currentLang = $accepted; + } + } +} +?> <!DOCTYPE html> -<html lang="en"> +<html lang="<?php echo htmlspecialchars($currentLang); ?>"> <head> - <meta charset="UTF-8" /> - <title>FakeUSBCheck – Test USB drives for fakes and hidden problems - - + + USBCheck – Test your USB drives + - - - - + + + + - - - - - + + - - -
+ - -
-
-
- - - FakeUSBCheck logo - + - - +
+ +
+
+
+

+

+

- -
- - +
+ - - + +
- - +
    +
  • +
  • +
  • +
+
+ +
+
+ +

+
+
+ + 125 MB/s +
+
+ + 99.98% +
+
+ + +
+
+

+
+
+ USB Stick Illustration
-
+ - -
+ +
+
+

+

- -
-
- -
- - - Detect fake USB drives before they destroy your data - - -

- Test your USB sticks for fakes, slow speed and - hidden errors. -

- -

- FakeUSBCheck helps you verify the real capacity and stability of USB flash drives. - Run a fast browser-based check in minutes or unlock the Pro mode for deep surface tests, - inspired by tools like f3 and badblocks. -

- - - - -
-
- - Browser-based quick test – nothing installed -
-
- - Pro mode with deep capacity verification -
-
-
- - -
-
-
-
-
-
-

- Quick USB check -

-

- Example device: 64 GB USB 3.1 flash drive -

-
- - - OK - -
- - -
-
-
- Write test - 145 MB/s -
-
-
-
-
-
-
- Read test - 180 MB/s -
-
-
-
-
-
-
- Integrity blocks checked - 8.0 GB of 8.0 GB -
-
-
-
-
-
- -
- Result: drive looks genuine - - Re-run test - -
-
- - - -
-
-
-
- - -
-
-
-

- How FakeUSBCheck works -

-

- FakeUSBCheck combines a safe, browser-based quick check with an optional Pro mode. - The browser mode focuses on performance and basic integrity. The Pro mode, supported - by a small helper app on your computer, can run deep capacity and surface tests similar - to established tools like f3 or badblocks. -

-
- -
-
-
- 1 -
-

Select your USB drive

-

- Choose the folder on your USB flash drive you want to test. The browser never - sees other drives or folders – you stay in control of what is accessed. -

-
-
-
- 2 -
-

Run the quick browser check

-

- FakeUSBCheck writes a configurable amount of test data, reads it back - and calculates checksums. You see write and read speeds, plus a basic - integrity result for the tested area. -

-
-
-
- 3 -
-

Unlock Pro mode for deep tests

-

- In Pro mode you install a small helper application that can perform full-drive - surface scans, fake capacity detection and extended stress tests, while - FakeUSBCheck provides the dashboard and reporting. -

-
-
-
-
- - -
-
-
-

- Designed for safety, speed and clarity -

-

- FakeUSBCheck focuses on giving you a clear result: is this drive worth trusting? - All tests are designed to be understandable, with transparent metrics and - optional in-depth reports for advanced users. -

-
- -
-
-

Quick browser check

-

- Start a quick test directly from your browser – no installation required. - Ideal before copying important data or using a new drive for the first time. -

-
    -
  • • Test presets: 200 MB, 2 GB, 8 GB
  • -
  • • Write & read throughput in MB/s
  • -
  • • Checksum-based integrity verification
  • -
-
- -
-

Pro capacity verification

-

- Detect fake capacity drives that claim unrealistic gigabytes or terabytes. - The Pro helper app can run full media scans while the web dashboard - visualizes progress and results. -

-
    -
  • • Detection of over-reported capacity
  • -
  • • Optional full surface scan
  • -
  • • JSON report & PDF export
  • -
-
- -
-

Clear reports

-

- Every test produces a human-readable summary and a technical report. - That makes it easy to document defective devices or prove that a drive - behaves as expected. -

-
    -
  • • Simple “OK / Warning / Failed” status
  • -
  • • Detailed logs for advanced users
  • -
  • • Optional sharing link for support
  • -
-
-
-
-
- - -
-
-
-

- Free quick check or Pro deep analysis -

-

- Start with the free browser-based quick check. If you need full-drive verification, repeated - test runs or PDF reports, upgrade to Pro. No subscription is required for basic checks. -

-
- -
- -
-

Free quick check

-

- Ideal for a fast sanity check of a new USB stick before you trust it with important files. -

-

€0

-
    -
  • • Browser-based test, no install
  • -
  • • Basic write & read speed measurement
  • -
  • • Configurable test size (up to a limit)
  • -
  • • Simple “OK / Warning / Failed” result
  • -
- - Start free quick check - -
- - -
-
- Coming soon -
-

Pro deep check

-

- For power users, IT support and anyone buying larger batches of USB drives who needs - reproducible, documented test results. -

-

Planned

-
    -
  • • Helper app for Windows, macOS & Linux
  • -
  • • Full-drive surface tests & capacity verification
  • -
  • • Detailed performance statistics and logs
  • -
  • • PDF and JSON export for documentation
  • -
- -
-
-
-
- - -
-
-
-
-

- Built with privacy and data safety in mind -

-

- Your data on the USB drive is sensitive. FakeUSBCheck is designed so that you stay in - control at every step. The browser-based quick check never uploads your test data. - Only aggregated test results are sent to our servers – and only if you explicitly agree. -

-

- The optional Pro helper runs locally on your device and only talks to our API using secure, - encrypted connections. You can review exactly what is being sent and revoke access at any - time. -

-

- We do not sell your test results to third parties. Anonymous statistics may be used to - improve detection of common fake models and to warn users about frequently abused brands. -

-
-
-

Security highlights

-
    -
  • • Browser quick check: test data stays on your device
  • -
  • • Optional upload of summarized results only
  • -
  • • Pro helper: TLS-encrypted communication
  • -
  • • Transparent logs so you can see what happened
  • -
  • • No hidden “phone home” behaviour
  • -
-

- Detailed privacy and security documentation will be available before the Pro mode - leaves beta. Our goal: you should be able to explain to a non-technical friend exactly - what FakeUSBCheck does – and what it doesn’t do. -

-
-
-
-
- - -
-
-
-

- Frequently asked questions -

-

- A few common questions about testing USB flash drives and how FakeUSBCheck fits into - your workflow. -

-
- -
-
-

- Can FakeUSBCheck damage my USB drive? -

-

- Any write test puts some wear on flash memory. The quick check is designed to use a - reasonable test size so that the wear stays minimal for healthy drives. The Pro mode - can perform heavy tests; those are intended for situations where you specifically want - to stress-test a drive or check a suspicious device. -

-
- -
-

- Do I have to install anything for the free quick check? -

-

- No. The browser-based quick check runs entirely inside your browser, using modern - APIs that allow you to work with files and folders on your USB drive with your - explicit consent. You only need to install the Pro helper if you want deep, - full-drive tests. -

-
- -
-

- How reliable is the detection of fake capacity drives? -

-

- Detecting fake capacities requires writing and reading large portions of the drive. - The Pro mode is specifically designed for this type of validation. While no tool can - guarantee 100% detection of all possible manipulation techniques, deep surface tests - provide strong evidence and clear documentation when a drive behaves incorrectly. -

-
- -
-

- Can I test external SSDs or only USB sticks? -

-

- The quick browser check focuses on USB flash drives, but in practice it works with - any storage that appears as a normal folder, including some external SSDs. The Pro - helper can work with a broader range of devices, including USB-connected SSDs and - some memory cards, depending on your operating system. -

-
-
-
-
- - -
-
-
-
-

- Ready to see if your USB sticks are real? -

-

- Start a free quick check in your browser. No account, no installation. If you need - more, Pro mode will be ready soon – with full-drive tests and exportable reports. -

-
- -
-
-
-
- - -
-
-

- © FakeUSBCheck. All rights reserved. -

-
- Imprint - Privacy - Terms +
+
+
1
+

+

+
+
+
2
+

+

+
+
+
3
+

+

+
+
+
4
+

+

+
-
-
+ - - +
+
+

+

+
    +
  • +
  • +
  • +
+ +
+
+

+

+
    +
  • +
  • +
  • +
+ +
+
+ + + + +
+
+

+

+ +
+
+ +

+
+
+ +

+
+
+ +

+
+
+ +

+
+
+
+
+ + + + + diff --git a/public/login.php b/public/login.php new file mode 100644 index 0000000..4f7eefe --- /dev/null +++ b/public/login.php @@ -0,0 +1,82 @@ +login($identifier, $password)) { + header('Location: /'); // nach Login auf Startseite + exit; + } else { + $error = 'Login fehlgeschlagen. Bitte Zugangsdaten prüfen.'; + } +} + +?> + + + + + Login – usbcheck.it + + + + + +
+
+

+ Anmelden bei usbcheck.it +

+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+ + +
+
+
+ + diff --git a/public/logout.php b/public/logout.php new file mode 100644 index 0000000..7b6116e --- /dev/null +++ b/public/logout.php @@ -0,0 +1,11 @@ +logout(); + +header('Location: /'); +exit; diff --git a/public/partial/header.php b/public/partial/header.php new file mode 100644 index 0000000..d4bbded --- /dev/null +++ b/public/partial/header.php @@ -0,0 +1,75 @@ + 'Deutsch', + 'en' => 'English', + 'it' => 'Italiano', + 'fr' => 'Français', +]; + +/** + * Baut eine URL und hängt immer ?lang= dran. + * $path sollte mit / beginnen, z.B. "/", "/fakecheck/", "/impressum". + */ +function usbcheck_url_with_lang(string $path, string $lang): string +{ + $path = $path ?: '/'; + $separator = str_contains($path, '?') ? '&' : '?'; + return $path . $separator . 'lang=' . urlencode($lang); +} +?> + diff --git a/public/register.php b/public/register.php new file mode 100644 index 0000000..217dd8b --- /dev/null +++ b/public/register.php @@ -0,0 +1,198 @@ + Accountseite + header('Location: /account.php?lang=' . urlencode($lang)); + exit; +} + +$errors = []; +$values = [ + 'email' => '', + 'username' => '', + 'full_name' => '', + 'preferred_lang' => $lang, +]; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (!auth_verify_csrf($_POST['csrf_token'] ?? null)) { + $errors['csrf'] = 'Deine Sitzung ist abgelaufen. Bitte Seite neu laden.'; + } else { + $email = $_POST['email'] ?? ''; + $username = $_POST['username'] ?? ''; + $fullName = $_POST['full_name'] ?? ''; + $password = $_POST['password'] ?? ''; + $passwordConfirm= $_POST['password_confirm'] ?? ''; + $preferredLang = $_POST['preferred_lang'] ?? $lang; + + $values = [ + 'email' => $email, + 'username' => $username, + 'full_name' => $fullName, + 'preferred_lang' => $preferredLang, + ]; + + $result = auth_register_user( + $email, + $username, + $fullName, + $password, + $passwordConfirm, + $preferredLang + ); + + if ($result['success']) { + header('Location: /account.php?lang=' . urlencode($preferredLang)); + exit; + } else { + $errors = array_merge($errors, $result['errors']); + } + } +} +?> + + + + + Registrierung – USBCheck + + + + + + + + + + + + + +
+
+
+

Konto erstellen

+

+ Erstelle ein kostenloses Konto, um deine USB-Tests zu verwalten. +

+ + +
+ +
+ + +
+ + +
+ + + +

+ +
+ +
+ + + +

+ +
+ +
+ + + +

+ +
+ +
+
+ + + +

+ +
+ +
+ + + +

+ +
+
+ +
+ + +
+ +
+ +

+ Du hast bereits ein Konto? + + Zum Login + +

+
+
+
+
+
+ + + + diff --git a/public/uploads/avatar/.gitkeep b/public/uploads/avatar/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/sql.schema b/sql.schema new file mode 100644 index 0000000..a2a5768 --- /dev/null +++ b/sql.schema @@ -0,0 +1,111 @@ +-- ============================================================ +-- USERS – Benutzerkonto + spätere Rechnungs-/Zahlungsinfos +-- ============================================================ + +CREATE TABLE IF NOT EXISTS users ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + + -- Login + username VARCHAR(50) NOT NULL UNIQUE, + email VARCHAR(150) NOT NULL UNIQUE, + password_hash VARCHAR(255) NOT NULL, + avatar_path VARCHAR(255) NULL, + + -- Persönliche Daten + first_name VARCHAR(100) NULL, + last_name VARCHAR(100) NULL, + + -- spätere Rechnungsdaten / Billing + company_name VARCHAR(255) NULL, + street VARCHAR(255) NULL, + postal_code VARCHAR(20) NULL, + city VARCHAR(255) NULL, + country VARCHAR(100) NULL, + vat_id VARCHAR(50) NULL, + + -- spätere Pro-Features / Limits + plan ENUM('free', 'pro', 'enterprise') DEFAULT 'free', + plan_valid_until DATETIME NULL, + + -- Sicherheit + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + last_login_at DATETIME NULL, + failed_logins INT DEFAULT 0, + is_locked TINYINT(1) DEFAULT 0 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + +-- ============================================================ +-- USB DEVICES – vom Nutzer gespeicherte USB-Sticks +-- Ein Benutzer kann mehrere Sticks speichern. +-- ============================================================ + +CREATE TABLE IF NOT EXISTS usb_devices ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + user_id BIGINT UNSIGNED NOT NULL, + + serial_number 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, + + capacity_bytes BIGINT UNSIGNED NULL, + advertised_capacity_bytes BIGINT UNSIGNED NULL, + + read_speed_mbps FLOAT NULL, + write_speed_mbps FLOAT NULL, + + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP + ON UPDATE CURRENT_TIMESTAMP, + + FOREIGN KEY (user_id) REFERENCES users(id) + ON DELETE CASCADE +); + + +-- ============================================================ +-- USB TEST RESULTS – Schnelltest + Pro-Test +-- Jedes Testergebnis gehört zu einem Stick. +-- ============================================================ + +CREATE TABLE IF NOT EXISTS usb_tests ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + usb_device_id BIGINT UNSIGNED NOT NULL, + user_id BIGINT UNSIGNED NOT NULL, + + -- Testtyp + test_type ENUM('quick', 'standard', 'deep', 'pro') NOT NULL, + + -- Ergebniswerte + test_start DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + test_end DATETIME NULL, + + read_speed_mbps FLOAT NULL, + write_speed_mbps FLOAT NULL, + + integrity_ok TINYINT(1) NULL, + checksum_sha256 VARCHAR(255) NULL, + + -- Pro-Modus Zusatzwerte (f3, badblocks etc.) + f3_status ENUM('unknown', 'pass', 'fail', 'warning') DEFAULT 'unknown', + f3_real_capacity_bytes BIGINT UNSIGNED NULL, + f3_lost_bytes BIGINT UNSIGNED NULL, + + badblocks_errors INT NULL, + + -- Metadaten + test_report_json JSON NULL, + ip_address VARCHAR(45) NULL, -- ipv6 kompatibel + + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP + ON UPDATE CURRENT_TIMESTAMP, + + FOREIGN KEY (usb_device_id) REFERENCES usb_devices(id) + ON DELETE CASCADE, + + FOREIGN KEY (user_id) REFERENCES users(id) + ON DELETE CASCADE +); diff --git a/src/Auth.php b/src/Auth.php new file mode 100644 index 0000000..44b049e --- /dev/null +++ b/src/Auth.php @@ -0,0 +1,284 @@ + 0, + 'path' => '/', + 'domain' => '', // Standard: aktuelle Domain + 'secure' => $secure, + 'httponly' => true, + 'samesite' => 'Lax', + ]); + session_start(); +} + +// --- Sprache ermitteln / speichern --- +function auth_get_lang(): string { + if (!empty($_GET['lang'])) { + $_SESSION['lang'] = $_GET['lang']; + } + if (!empty($_SESSION['lang'])) { + return $_SESSION['lang']; + } + return 'en'; +} + +// --- CSRF-Token --- +function auth_csrf_token(): string { + if (empty($_SESSION['csrf_token'])) { + $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); + } + return $_SESSION['csrf_token']; +} + +function auth_verify_csrf(?string $token): bool { + if (empty($token) || empty($_SESSION['csrf_token'])) { + return false; + } + return hash_equals($_SESSION['csrf_token'], $token); +} + +// --- PDO Helper --- +function auth_pdo(): PDO { + // $pdo kommt aus config/db.php + global $pdo; + if (!$pdo instanceof PDO) { + throw new RuntimeException('Database connection not available.'); + } + return $pdo; +} + +// --- Aktueller User --- +function auth_current_user(): ?array { + if (!empty($_SESSION['user_cache']) && is_array($_SESSION['user_cache'])) { + return $_SESSION['user_cache']; + } + if (empty($_SESSION['user_id'])) { + return null; + } + $pdo = auth_pdo(); + $stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id LIMIT 1'); + $stmt->execute([':id' => $_SESSION['user_id']]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + if (!$user) { + return null; + } + $_SESSION['user_cache'] = $user; + return $user; +} + +function auth_require_login(): void { + if (!auth_current_user()) { + $lang = auth_get_lang(); + header('Location: /login.php?lang=' . urlencode($lang)); + exit; + } +} + +// --- Avatar-Helfer --- +function auth_user_initials(array $user): string { + $name = $user['full_name'] ?? ''; + if (trim($name) === '') { + $name = $user['username'] ?? $user['email'] ?? 'U'; + } + $parts = preg_split('/\s+/', trim($name)); + $initials = strtoupper(mb_substr($parts[0], 0, 1)); + if (count($parts) > 1) { + $initials .= strtoupper(mb_substr(end($parts), 0, 1)); + } + return $initials; +} + +function auth_user_avatar_url(array $user): ?string { + if (!empty($user['avatar_path'])) { + return '/uploads/avatars/' . ltrim($user['avatar_path'], '/'); + } + return null; +} + +// --- Registrierung --- +function auth_register_user( + string $email, + string $username, + string $fullName, + string $password, + string $passwordConfirm, + string $preferredLang +): array { + $pdo = auth_pdo(); + $errors = []; + + $email = trim($email); + $username = trim($username); + $fullName = trim($fullName); + + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + $errors['email'] = 'Bitte eine gültige E-Mail-Adresse eingeben.'; + } + + if ($username === '' || !preg_match('/^[a-zA-Z0-9_.-]{3,32}$/', $username)) { + $errors['username'] = 'Username muss 3–32 Zeichen lang sein und darf nur Buchstaben, Zahlen, ., _, - enthalten.'; + } + + if (mb_strlen($fullName) < 3) { + $errors['full_name'] = 'Bitte einen vollständigen Namen angeben.'; + } + + if (mb_strlen($password) < 10) { + $errors['password'] = 'Passwort muss mindestens 10 Zeichen lang sein.'; + } + + if ($password !== $passwordConfirm) { + $errors['password_confirm'] = 'Passwörter stimmen nicht überein.'; + } + + $allowedLangs = ['de', 'en', 'it', 'fr']; + if (!in_array($preferredLang, $allowedLangs, true)) { + $preferredLang = 'en'; + } + + // E-Mail / Username bereits vergeben? + if (!$errors) { + $stmt = $pdo->prepare('SELECT email, username FROM users WHERE email = :email OR username = :username LIMIT 1'); + $stmt->execute([ + ':email' => $email, + ':username' => $username, + ]); + $existing = $stmt->fetch(PDO::FETCH_ASSOC); + if ($existing) { + if (strcasecmp($existing['email'], $email) === 0) { + $errors['email'] = 'Diese E-Mail-Adresse wird bereits verwendet.'; + } + if (strcasecmp($existing['username'], $username) === 0) { + $errors['username'] = 'Dieser Username ist bereits vergeben.'; + } + } + } + + if ($errors) { + return ['success' => false, 'errors' => $errors]; + } + + $hash = password_hash($password, PASSWORD_DEFAULT); + $now = (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format('Y-m-d H:i:s'); + + $stmt = $pdo->prepare(' + INSERT INTO users (email, username, full_name, password_hash, preferred_lang, created_at, updated_at) + VALUES (:email, :username, :full_name, :password_hash, :preferred_lang, :created_at, :updated_at) + '); + $stmt->execute([ + ':email' => $email, + ':username' => $username, + ':full_name' => $fullName, + ':password_hash' => $hash, + ':preferred_lang'=> $preferredLang, + ':created_at' => $now, + ':updated_at' => $now, + ]); + + $userId = (int)$pdo->lastInsertId(); + $_SESSION['user_id'] = $userId; + unset($_SESSION['user_cache']); // neu laden beim nächsten Zugriff + $_SESSION['lang'] = $preferredLang; + + return ['success' => true, 'errors' => []]; +} + +// --- Login --- +function auth_login(string $login, string $password): array { + $pdo = auth_pdo(); + $errors = []; + + $login = trim($login); + + if ($login === '' || $password === '') { + $errors['login'] = 'Bitte Zugangsdaten vollständig ausfüllen.'; + return ['success' => false, 'errors' => $errors]; + } + + $stmt = $pdo->prepare(' + SELECT * FROM users + WHERE email = :login OR username = :login + LIMIT 1 + '); + $stmt->execute([':login' => $login]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$user || !password_verify($password, $user['password_hash'])) { + $errors['login'] = 'E-Mail/Username oder Passwort ist falsch.'; + return ['success' => false, 'errors' => $errors]; + } + + $_SESSION['user_id'] = (int)$user['id']; + unset($_SESSION['user_cache']); + if (!empty($user['preferred_lang'])) { + $_SESSION['lang'] = $user['preferred_lang']; + } + + // Option: Password-Rehash, wenn Algorithmus veraltet + if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT)) { + $newHash = password_hash($password, PASSWORD_DEFAULT); + $upd = $pdo->prepare('UPDATE users SET password_hash = :hash WHERE id = :id'); + $upd->execute([':hash' => $newHash, ':id' => $user['id']]); + } + + return ['success' => true, 'errors' => []]; +} + +// --- Profil aktualisieren (Name, Sprache) --- +function auth_update_profile(int $userId, string $fullName, string $preferredLang): array { + $pdo = auth_pdo(); + $errors = []; + + $fullName = trim($fullName); + if (mb_strlen($fullName) < 3) { + $errors['full_name'] = 'Bitte einen gültigen Namen angeben.'; + } + + $allowedLangs = ['de', 'en', 'it', 'fr']; + if (!in_array($preferredLang, $allowedLangs, true)) { + $preferredLang = 'en'; + } + + if ($errors) { + return ['success' => false, 'errors' => $errors]; + } + + $now = (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format('Y-m-d H:i:s'); + $stmt = $pdo->prepare(' + UPDATE users + SET full_name = :full_name, + preferred_lang = :preferred_lang, + updated_at = :updated_at + WHERE id = :id + '); + $stmt->execute([ + ':full_name' => $fullName, + ':preferred_lang'=> $preferredLang, + ':updated_at' => $now, + ':id' => $userId, + ]); + + unset($_SESSION['user_cache']); + $_SESSION['lang'] = $preferredLang; + + return ['success' => true, 'errors' => []]; +} + +// --- Logout --- +function auth_logout(): void { + $_SESSION = []; + if (ini_get('session.use_cookies')) { + $params = session_get_cookie_params(); + setcookie(session_name(), '', time() - 42000, $params['path'], $params['domain'], $params['secure'], $params['httponly']); + } + session_destroy(); +} diff --git a/src/Database.php b/src/Database.php new file mode 100644 index 0000000..1dc89a9 --- /dev/null +++ b/src/Database.php @@ -0,0 +1,25 @@ + 0, + 'path' => '/', + 'secure' => isset($_SERVER['HTTPS']), + 'httponly' => true, + 'samesite' => 'Lax', + ]); + session_start(); + } + } + + public static function regenerate(): void + { + if (session_status() === PHP_SESSION_ACTIVE) { + session_regenerate_id(true); + } + } + + public static function set(string $key, mixed $value): void + { + $_SESSION[$key] = $value; + } + + public static function get(string $key, mixed $default = null): mixed + { + return $_SESSION[$key] ?? $default; + } + + public static function remove(string $key): void + { + unset($_SESSION[$key]); + } + + public static function destroy(): void + { + if (session_status() === PHP_SESSION_ACTIVE) { + $_SESSION = []; + if (ini_get("session.use_cookies")) { + $params = session_get_cookie_params(); + setcookie( + session_name(), + '', + time() - 42000, + $params["path"], + $params["domain"], + $params["secure"], + $params["httponly"] + ); + } + session_destroy(); + } + } + + public static function csrfToken(): string + { + self::start(); + if (!isset($_SESSION['_csrf_token'])) { + $_SESSION['_csrf_token'] = bin2hex(random_bytes(32)); + } + return $_SESSION['_csrf_token']; + } + + public static function validateCsrf(?string $token): bool + { + self::start(); + if (!isset($_SESSION['_csrf_token']) || !$token) { + return false; + } + $valid = hash_equals($_SESSION['_csrf_token'], $token); + if ($valid) { + // Optional: Token nach Benutzung rotieren + unset($_SESSION['_csrf_token']); + } + return $valid; + } +}