ab die maus

This commit is contained in:
2025-11-22 02:56:15 +01:00
parent 7411aafe76
commit d45e971ae0
9 changed files with 160 additions and 62 deletions

View File

@@ -1,5 +1,5 @@
<?php <?php
// public/partials/header.php // partials/structure/header.php
// Aktuelle Domain + Protokoll // Aktuelle Domain + Protokoll
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
@@ -66,9 +66,10 @@ if ($isLoggedIn) {
</a> </a>
</div> </div>
<!-- Navigation --> <!-- Navigation + Controls -->
<div class="flex items-center gap-6"> <div class="flex items-center gap-6">
<!-- Hauptnavigation -->
<nav class="md:flex items-center gap-6 text-xs font-medium text-brand-muted uppercase tracking-[0.18em]"> <nav class="md:flex items-center gap-6 text-xs font-medium text-brand-muted uppercase tracking-[0.18em]">
<?php foreach ($navAnchors as $item): ?> <?php foreach ($navAnchors as $item): ?>
<a href="<?= htmlspecialchars($item['href']) ?>" <a href="<?= htmlspecialchars($item['href']) ?>"
@@ -127,12 +128,30 @@ if ($isLoggedIn) {
Login Login
</button> </button>
<?php else: ?> <?php else: ?>
<!-- Eingeloggt: Avatar anzeigen --> <!-- Eingeloggt: Avatar + User-Menü -->
<div class="relative">
<button id="userAvatar" <button id="userAvatar"
type="button"
class="h-9 w-9 rounded-full border border-brand-border bg-brand-surface flex items-center justify-center text-xs font-semibold text-brand-text shadow-soft hover:border-brand-primary transition" class="h-9 w-9 rounded-full border border-brand-border bg-brand-surface flex items-center justify-center text-xs font-semibold text-brand-text shadow-soft hover:border-brand-primary transition"
aria-haspopup="true"
aria-expanded="false"
aria-label="Mein Konto"> aria-label="Mein Konto">
<span><?= htmlspecialchars($userInitials) ?></span> <span><?= htmlspecialchars($userInitials) ?></span>
</button> </button>
<div id="userMenu"
class="hidden absolute right-0 mt-2 w-40 rounded-xl bg-brand-surface border border-brand-border shadow-lg py-1 text-xs text-brand-muted">
<a href="/dashboard/?lang=<?= urlencode($lang) ?>"
class="flex items-center gap-2 px-3 py-1.5 hover:bg-brand-bg/60 hover:text-brand-primary transition-colors">
<span>Dashboard</span>
</a>
<a href="/auth/logout?lang=<?= urlencode($lang) ?>"
id="userMenuLogout"
class="flex items-center gap-2 px-3 py-1.5 hover:bg-brand-bg/60 hover:text-red-300 transition-colors">
<span>Logout</span>
</a>
</div>
</div>
<?php endif; ?> <?php endif; ?>
</div> </div>

View File

@@ -20,6 +20,8 @@ if (!isset($pageDescription) || !is_string($pageDescription)) {
$pageDescription = ''; $pageDescription = '';
} }
tpl_add_script('/assets/js/header-user-menu.js', 'footer', true, false, '', null);
// Kann später genutzt werden, falls du host-spezifische Sachen brauchst // Kann später genutzt werden, falls du host-spezifische Sachen brauchst
$host = $_SERVER['HTTP_HOST'] ?? ''; $host = $_SERVER['HTTP_HOST'] ?? '';
?> ?>

View File

@@ -0,0 +1,35 @@
document.addEventListener('DOMContentLoaded', function () {
var avatarBtn = document.getElementById('userAvatar');
var userMenu = document.getElementById('userMenu');
var logoutLink = document.getElementById('userMenuLogout');
if (avatarBtn && userMenu) {
// Avatar klick → Menü toggeln
avatarBtn.addEventListener('click', function (e) {
e.stopPropagation();
userMenu.classList.toggle('hidden');
});
// Klick in Menü nicht nach außen „bubblen“
userMenu.addEventListener('click', function (e) {
e.stopPropagation();
});
// Klick irgendwo außerhalb → Menü schließen
document.addEventListener('click', function () {
if (!userMenu.classList.contains('hidden')) {
userMenu.classList.add('hidden');
}
});
}
// Logout mit Confirm
if (logoutLink) {
logoutLink.addEventListener('click', function (e) {
var ok = confirm('Möchtest du dich wirklich abmelden?');
if (!ok) {
e.preventDefault();
}
});
}
});

View File

@@ -1,4 +1,4 @@
<?php <?php
// Gemeinsame Bootstrap-/Config-Datei laden // Gemeinsame Bootstrap-/Config-Datei laden
require __DIR__ . '/../../config/fileload.php'; // Pfad ggf. anpassen //require __DIR__ . '/../../config/fileload.php'; // Pfad ggf. anpassen
require __DIR__ . '/../../src/auth/login.php'; require_once __DIR__ . '/../../src/auth/login.php';

View File

@@ -1,4 +1,4 @@
<?php <?php
// Gemeinsame Bootstrap-/Config-Datei laden // Gemeinsame Bootstrap-/Config-Datei laden
require __DIR__ . '/../../config/fileload.php'; // Pfad ggf. anpassen //require __DIR__ . '/../../config/fileload.php'; // Pfad ggf. anpassen
require __DIR__ . '/../../src/auth/logout.php'; require_once __DIR__ . '/../../src/auth/logout.php';

View File

@@ -1,4 +1,4 @@
<?php <?php
// Gemeinsame Bootstrap-/Config-Datei laden // Gemeinsame Bootstrap-/Config-Datei laden
require __DIR__ . '/../../config/fileload.php'; // Pfad ggf. anpassen //require __DIR__ . '/../../config/fileload.php'; // Pfad ggf. anpassen
require __DIR__ . '/../../src/auth/register.php'; require_once __DIR__ . '/../../src/auth/register.php';

View File

@@ -1,29 +1,34 @@
<?php <?php
// public/auth/login.php // src/auth/login.php
// wird durch public/auth/login.php aufgerufen
// Nur POST zulassen
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
// Direktzugriff per GET → einfach zurück zur Login-Seite
$lang = $_GET['lang'] ?? 'de'; $lang = $_GET['lang'] ?? 'de';
header('Location: /login/?lang=' . urlencode($lang) . '&view=login#auth'); header('Location: /login/?lang=' . urlencode($lang) . '&view=login#auth');
exit; exit;
} }
// Daten einsammeln // Basis-Setup: Session, $pdo, flash_*, Config etc.
require_once __DIR__ . '/../../config/fileload.php';
// Form-Daten einsammeln
$lang = $_POST['lang'] ?? 'de'; $lang = $_POST['lang'] ?? 'de';
$email = trim((string)($_POST['email'] ?? '')); $email = trim((string)($_POST['email'] ?? ''));
$password = (string)($_POST['password'] ?? ''); $password = (string)($_POST['password'] ?? '');
$redirect = $_POST['redirect'] ?? '/'; $redirect = $_POST['redirect'] ?? '/';
// Minimale Validierung // Minimal-Validierung
if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL) || $password === '') { if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL) || $password === '') {
flash_set('error', 'Bitte E-Mail-Adresse und Passwort eingeben.', 'login'); flash_set('error', 'Bitte E-Mail-Adresse und Passwort eingeben.', 'login');
header('Location: /login/?lang=' . urlencode($lang) . '&view=login#auth'); header('Location: /login/?lang=' . urlencode($lang) . '&view=login#auth');
exit; exit;
} }
// Annahme: $pdo ist in fileload.php / db.php gesetzt (PDO-Instanz) /* ---------------------------------------------------------
USER LADEN
--------------------------------------------------------- */
try { try {
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email LIMIT 1'); $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email LIMIT 1');
$stmt->execute([':email' => $email]); $stmt->execute([':email' => $email]);
@@ -41,20 +46,27 @@ if (!$user) {
exit; exit;
} }
// Account gesperrt? /* ---------------------------------------------------------
ACCOUNT GESPERRT?
--------------------------------------------------------- */
if (!empty($user['is_locked'])) { if (!empty($user['is_locked'])) {
flash_set('error', 'Dein Konto ist gesperrt. Bitte kontaktiere den Support.', 'login'); flash_set('error', 'Dein Konto ist gesperrt. Bitte kontaktiere den Support.', 'login');
header('Location: /login/?lang=' . urlencode($lang) . '&view=login#auth'); header('Location: /login/?lang=' . urlencode($lang) . '&view=login#auth');
exit; exit;
} }
// Passwort prüfen /* ---------------------------------------------------------
PASSWORT PRÜFEN
--------------------------------------------------------- */
if (!password_verify($password, $user['password_hash'])) { if (!password_verify($password, $user['password_hash'])) {
// Fehlversuche hochzählen (optional)
// Fehlversuche hochzählen
try { try {
$failed = (int)($user['failed_logins'] ?? 0) + 1; $failed = (int)($user['failed_logins'] ?? 0) + 1;
$lock = 0; $lock = 0;
if ($failed >= 5) { if ($failed >= 5) {
$lock = 1; $lock = 1;
} }
@@ -66,7 +78,7 @@ if (!password_verify($password, $user['password_hash'])) {
':id' => $user['id'], ':id' => $user['id'],
]); ]);
} catch (Throwable $e) { } catch (Throwable $e) {
// Ignorieren, Login-Fehler reicht // Nicht kritisch, Meldung reicht
} }
$msg = 'E-Mail oder Passwort ist falsch.'; $msg = 'E-Mail oder Passwort ist falsch.';
@@ -79,7 +91,10 @@ if (!password_verify($password, $user['password_hash'])) {
exit; exit;
} }
// Passwort korrekt → Fehlversuche zurücksetzen & letztes Login speichern /* ---------------------------------------------------------
PASSWORT KORREKT → FAILED_LOGINS RESET + LAST_LOGIN
--------------------------------------------------------- */
try { try {
$upd = $pdo->prepare('UPDATE users SET failed_logins = 0, last_login_at = NOW() WHERE id = :id'); $upd = $pdo->prepare('UPDATE users SET failed_logins = 0, last_login_at = NOW() WHERE id = :id');
$upd->execute([':id' => $user['id']]); $upd->execute([':id' => $user['id']]);
@@ -87,12 +102,10 @@ try {
// Nicht kritisch für den Nutzer // Nicht kritisch für den Nutzer
} }
// Session füllen /* ---------------------------------------------------------
if (session_status() !== PHP_SESSION_ACTIVE) { SESSION FÜLLEN
@session_start(); --------------------------------------------------------- */
}
// Initialen bauen
$firstName = $user['first_name'] ?? ''; $firstName = $user['first_name'] ?? '';
$lastName = $user['last_name'] ?? ''; $lastName = $user['last_name'] ?? '';
$initials = ''; $initials = '';
@@ -118,16 +131,19 @@ $_SESSION['user'] = [
'initials' => $initials, 'initials' => $initials,
]; ];
// Flash für „Willkommen“ /* ---------------------------------------------------------
FLASH & REDIRECT
--------------------------------------------------------- */
flash_set('success', 'Willkommen zurück, ' . ($user['first_name'] ?: 'User') . '!', 'login'); flash_set('success', 'Willkommen zurück, ' . ($user['first_name'] ?: 'User') . '!', 'login');
// Redirect-Ziel prüfen (nur interne Pfade erlauben) // Redirect absichern: nur interne Pfade
$target = is_string($redirect) ? trim($redirect) : '/'; $target = is_string($redirect) ? trim($redirect) : '/';
if ($target === '' || $target[0] !== '/') { if ($target === '' || !str_starts_with($target, '/')) {
$target = '/'; $target = '/';
} }
// Sprache ggf. anfügen, wenn noch nicht als Parameter vorhanden // Sprache anhängen
$sep = (strpos($target, '?') === false) ? '?' : '&'; $sep = (strpos($target, '?') === false) ? '?' : '&';
$target = $target . $sep . 'lang=' . urlencode($lang); $target = $target . $sep . 'lang=' . urlencode($lang);

View File

@@ -1,20 +1,22 @@
<?php <?php
// public/auth/logout.php // src/auth/logout.php
// wird durch public/auth/logout.php aufgerufen
if (session_status() !== PHP_SESSION_ACTIVE) { require_once __DIR__ . '/../../config/fileload.php';
@session_start();
}
// Session leeren, aber NICHT komplett zerstören, // Session läuft bereits durch fileload.php
// damit flash_set noch funktionieren kann. // → wir müssen sie nur leeren, nicht löschen
// Session leeren (aber nicht zerstören flash_set muss funktionieren)
$_SESSION = []; $_SESSION = [];
session_regenerate_id(true); session_regenerate_id(true);
// Sprache aus GET, falls vorhanden // Sprache mitnehmen
$lang = $_GET['lang'] ?? 'de'; $lang = $_GET['lang'] ?? 'de';
// Flash-Meldung anzeigen
flash_set('success', 'Du wurdest erfolgreich ausgeloggt.', 'login'); flash_set('success', 'Du wurdest erfolgreich ausgeloggt.', 'login');
// Zur Startseite zurück // Redirect zur Startseite
header('Location: /?lang=' . urlencode($lang)); header('Location: /?lang=' . urlencode($lang));
exit; exit;

View File

@@ -1,5 +1,6 @@
<?php <?php
// public/auth/register.php // src/auth/register.php
// Wird durch public/auth/register.php aufgerufen
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
$lang = $_GET['lang'] ?? 'de'; $lang = $_GET['lang'] ?? 'de';
@@ -7,12 +8,19 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit; exit;
} }
// Benötigt: $pdo, flash(), Session → via fileload
require_once __DIR__ . '/../../config/fileload.php';
$lang = $_POST['lang'] ?? 'de'; $lang = $_POST['lang'] ?? 'de';
$name = trim((string)($_POST['name'] ?? '')); $name = trim((string)($_POST['name'] ?? ''));
$email = trim((string)($_POST['email'] ?? '')); $email = trim((string)($_POST['email'] ?? ''));
$password = (string)($_POST['password'] ?? ''); $password = (string)($_POST['password'] ?? '');
$redirect = $_POST['redirect'] ?? '/'; $redirect = $_POST['redirect'] ?? '/';
/* ---------------------------------------------------------
VALIDIERUNG
--------------------------------------------------------- */
if ($name === '' || $email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) { if ($name === '' || $email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
flash_set('error', 'Bitte einen gültigen Namen und eine gültige E-Mail-Adresse eingeben.', 'register'); flash_set('error', 'Bitte einen gültigen Namen und eine gültige E-Mail-Adresse eingeben.', 'register');
header('Location: /login/?lang=' . urlencode($lang) . '&view=register#auth'); header('Location: /login/?lang=' . urlencode($lang) . '&view=register#auth');
@@ -25,13 +33,17 @@ if (strlen($password) < 8) {
exit; exit;
} }
// Prüfen, ob E-Mail bereits existiert
/* ---------------------------------------------------------
PRÜFEN, OB E-MAIL SCHON EXISTIERT
--------------------------------------------------------- */
try { try {
$stmt = $pdo->prepare('SELECT id FROM users WHERE email = :email LIMIT 1'); $stmt = $pdo->prepare('SELECT id FROM users WHERE email = :email LIMIT 1');
$stmt->execute([':email' => $email]); $stmt->execute([':email' => $email]);
$existing = $stmt->fetch(PDO::FETCH_ASSOC); $existing = $stmt->fetch(PDO::FETCH_ASSOC);
} catch (Throwable $e) { } catch (Throwable $e) {
flash_set('error', 'Es ist ein Fehler bei der Registrierung aufgetreten. Bitte versuche es später erneut.', 'register'); flash_set('error', 'Es ist ein technischer Fehler aufgetreten. Bitte später erneut versuchen.', 'register');
header('Location: /login/?lang=' . urlencode($lang) . '&view=register#auth'); header('Location: /login/?lang=' . urlencode($lang) . '&view=register#auth');
exit; exit;
} }
@@ -42,10 +54,11 @@ if ($existing) {
exit; exit;
} }
// Username aus E-Mail ableiten (oder einfach die komplette E-Mail nutzen)
$username = $email;
// Vor- und Nachname grob aus dem „Name“-Feld splitten /* ---------------------------------------------------------
NAME AUFTEILEN (Vorname / Nachname)
--------------------------------------------------------- */
$firstName = $name; $firstName = $name;
$lastName = null; $lastName = null;
@@ -55,6 +68,11 @@ if (count($parts) >= 2) {
$lastName = implode(' ', $parts); $lastName = implode(' ', $parts);
} }
/* ---------------------------------------------------------
USER ANLEGEN
--------------------------------------------------------- */
$username = $email;
$passwordHash = password_hash($password, PASSWORD_DEFAULT); $passwordHash = password_hash($password, PASSWORD_DEFAULT);
try { try {
@@ -80,10 +98,10 @@ try {
exit; exit;
} }
// Direkt einloggen
if (session_status() !== PHP_SESSION_ACTIVE) { /* ---------------------------------------------------------
@session_start(); DIREKT EINLOGGEN
} --------------------------------------------------------- */
$initials = ''; $initials = '';
if ($firstName !== '') { if ($firstName !== '') {
@@ -107,16 +125,22 @@ $_SESSION['user'] = [
'initials' => $initials, 'initials' => $initials,
]; ];
/* ---------------------------------------------------------
ERFOLGSMELDUNG + REDIRECT
--------------------------------------------------------- */
flash_set('success', 'Konto erfolgreich erstellt. Willkommen bei USBCheck!', 'login'); flash_set('success', 'Konto erfolgreich erstellt. Willkommen bei USBCheck!', 'login');
// Redirect-Ziel prüfen (nur interne Pfade) // Redirect absichern: nur interne Pfade erlauben
$target = is_string($redirect) ? trim($redirect) : '/'; $target = is_string($redirect) ? trim($redirect) : '/';
if ($target === '' || $target[0] !== '/') { if ($target === '' || !str_starts_with($target, '/')) {
$target = '/'; $target = '/';
} }
$sep = (strpos($target, '?') === false) ? '?' : '&'; $separator = (strpos($target, '?') === false) ? '?' : '&';
$target = $target . $sep . 'lang=' . urlencode($lang); $target .= $separator . 'lang=' . urlencode($lang);
header('Location: ' . $target); header('Location: ' . $target);
exit; exit;