diff --git a/assets/app_auth.js b/assets/app_auth.js index 9a1102d..0efec5f 100644 --- a/assets/app_auth.js +++ b/assets/app_auth.js @@ -1,267 +1,390 @@ /** - * 🔐 AUTHENTIFIZIERUNG, USER MANAGEMENT & SINGLE-USER-MODUS 🔐 - * ============================================================== - * Unterstützt: - * 1. 🚀 Single-User Gast-Modus (1-Klick Start ohne Registrierung, 100% offline-fähig) - * 2. 🎓 Solo-Selbstlerner Konto (Cloud-Save, Streaks, XP & Zertifikate ohne Klassenbindung) - * 3. 🏫 Schulklassen-Modus (Schüler & Lehrer mit Lernmatrix) + * 🔐 AUTHENTIFIZIERUNG & ZUGANGSSCHUTZ (AUTH GUARD) 🔐 + * ======================================================= + * IT-Praxisportal – Benutzer-Authentifizierung und Schutz aller Kurse & Web-IDE. + * Zugriff auf Kurse, Lerneinheiten und Web-IDE erfordert ein registriertes Benutzerkonto. */ (function () { + 'use strict'; + function escapeHtml(text) { - if (!text) return ""; - return String(text).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); + if (!text) return ''; + return String(text) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + // Initialisiere Auth-Objekt + const initialUser = JSON.parse(localStorage.getItem('auth_user') || 'null'); + // Alte Gast-Sitzungen ungültig machen + if (initialUser && initialUser.isGuest) { + localStorage.removeItem('auth_user'); + localStorage.removeItem('auth_token'); } window.AUTH = { - token: localStorage.getItem("auth_token") || null, - user: JSON.parse(localStorage.getItem("auth_user") || "null"), + token: localStorage.getItem('auth_token') || null, + user: (initialUser && !initialUser.isGuest) ? initialUser : null, - // 1. GAST / SINGLE-USER MODUS (Sofort loslegen ohne Login) - startGuestSoloMode() { - const guestUser = { - id: "solo_guest", - name: "Solo-Entwickler", - email: "gast@lokal", - role: "solo", - xp: 0, - level: 1, - isGuest: true - }; - this.setSession("guest_token_" + Date.now(), guestUser); - if (document.getElementById("auth-modal-overlay")) { - document.getElementById("auth-modal-overlay").remove(); - } - this.updateUI(); - return guestUser; - }, - - // 2. REGISTRIERUNG - async register(name, email, password, role = "solo") { - try { - const res = await fetch("/api/auth/register", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ name, email, password, role }) - }); - const data = await res.json(); - if (!res.ok) throw new Error(data.error || "Registrierung fehlgeschlagen"); - - this.setSession(data.token, data.user); - return { success: true, user: data.user }; - } catch (err) { - // Fallback für statische Offline-Nutzung - const mockUser = { id: Date.now(), name, email, role, xp: 0, level: 1 }; - this.setSession("offline_token_" + Date.now(), mockUser); - return { success: true, user: mockUser, offline: true }; - } - }, - - // 3. LOGIN - async login(email, password) { - try { - const res = await fetch("/api/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email, password }) - }); - const data = await res.json(); - if (!res.ok) throw new Error(data.error || "Anmeldung fehlgeschlagen"); - - this.setSession(data.token, data.user); - return { success: true, user: data.user }; - } catch (err) { - const mockUser = { id: 1, name: email.split("@")[0], email, role: "solo", xp: 150, level: 2 }; - this.setSession("offline_token", mockUser); - return { success: true, user: mockUser, offline: true }; - } - }, - - logout() { - this.token = null; - this.user = null; - localStorage.removeItem("auth_token"); - localStorage.removeItem("auth_user"); - window.location.reload(); + isLoggedIn() { + return !!(this.token && this.user && !this.user.isGuest && this.user.email); }, setSession(token, user) { this.token = token; this.user = user; - localStorage.setItem("auth_token", token); - localStorage.setItem("auth_user", JSON.stringify(user)); + localStorage.setItem('auth_token', token); + localStorage.setItem('auth_user', JSON.stringify(user)); this.updateUI(); }, - async fetchMe() { - if (!this.token || (this.user && this.user.isGuest)) return this.user; + logout() { + this.token = null; + this.user = null; + localStorage.removeItem('auth_token'); + localStorage.removeItem('auth_user'); + this.updateUI(); + + // Wenn auf einer geschützten Kursseite, zur Startseite leiten + const p = window.location.pathname; + if (p.includes('/courses/') || p.includes('/lehrpfad_') || p.endsWith('workspace.html')) { + window.location.href = window.location.origin + '/index.html?auth=login'; + } + }, + + async register(name, email, password, role = 'solo') { try { - const res = await fetch("/api/auth/me", { - headers: { Authorization: `Bearer ${this.token}` } + const res = await fetch('/api/auth/register', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name, email, password, role }) + }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error || 'Registrierung fehlgeschlagen'); + + this.setSession(data.token, data.user); + return { success: true, user: data.user }; + } catch (err) { + // Lokaler Fallback falls Backend temporär nicht erreichbar + console.warn('API-Registrierung fehlgeschlagen, nutze lokalen Speicher:', err.message); + const localUser = { id: Date.now(), name, email, role, xp: 0, level: 1 }; + this.setSession('local_token_' + Date.now(), localUser); + return { success: true, user: localUser }; + } + }, + + async login(email, password) { + try { + const res = await fetch('/api/auth/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, password }) + }); + const data = await res.json(); + if (!res.ok) throw new Error(data.error || 'Anmeldung fehlgeschlagen'); + + this.setSession(data.token, data.user); + return { success: true, user: data.user }; + } catch (err) { + console.warn('API-Login fehlgeschlagen, versuche lokalen Fallback:', err.message); + if (email && password && password.length >= 6) { + const localUser = { id: 1, name: email.split('@')[0], email, role: 'solo', xp: 100, level: 1 }; + this.setSession('local_token_' + Date.now(), localUser); + return { success: true, user: localUser }; + } + return { success: false, error: err.message || 'Ungültige Anmeldedaten' }; + } + }, + + async fetchMe() { + if (!this.token) return; + try { + const res = await fetch('/api/auth/me', { + headers: { 'Authorization': `Bearer ${this.token}` } }); if (res.ok) { const data = await res.json(); this.user = data.user; - localStorage.setItem("auth_user", JSON.stringify(data.user)); + localStorage.setItem('auth_user', JSON.stringify(data.user)); this.updateUI(); - return data.user; } - } catch (e) {} - return this.user; + } catch (e) { + // Offline / Netzwerkfehler ignorieren + } + }, + + requireAuth(options = {}) { + if (this.isLoggedIn()) { + return true; + } + this.showAuthBarrier(options); + return false; + }, + + showAuthBarrier(options = {}) { + window.openAuthModal(options.mode || 'register', { + allowClose: options.allowClose !== undefined ? options.allowClose : false, + title: options.title || '🔒 Registrierung erforderlich', + message: options.message || 'Dieser Kursbereich und die Web-IDE sind exklusiv für registrierte Teilnehmer. Erstelle einen kostenlosen Account oder melde dich an, um sofort zu starten.', + onSuccess: () => { + if (options.onSuccess) options.onSuccess(); + else window.location.reload(); + } + }); }, updateUI() { - const userBadge = document.getElementById("nav-auth-user"); - const loginBtn = document.getElementById("nav-btn-login"); - const registerBtn = document.getElementById("nav-btn-register"); - const teacherLink = document.getElementById("nav-teacher-link"); - const soloModeBadge = document.getElementById("solo-mode-indicator"); + const loggedIn = this.isLoggedIn(); - if (this.user) { - if (userBadge) { - userBadge.style.display = "inline-flex"; - const roleLabel = this.user.role === "teacher" ? "👨🏫 Lehrer" : (this.user.role === "solo" ? "🎓 Solo" : "🎒 Schüler"); - userBadge.innerHTML = `👤 ${escapeHtml(this.user.name)} (${roleLabel}) ${this.user.xp || 0} XP`; + // Topbar Elemente in index.html + const userSpan = document.getElementById('nav-auth-user'); + const loginBtn = document.getElementById('nav-btn-login'); + const registerBtn = document.getElementById('nav-btn-register'); + const guestBtn = document.getElementById('nav-btn-guest'); + const teacherLink = document.getElementById('nav-teacher-link'); + const heroPrimaryCta = document.getElementById('hero-primary-cta'); + + if (guestBtn) guestBtn.style.display = 'none'; // Gast-Button permanent ausblenden + + if (loggedIn && this.user) { + if (userSpan) { + userSpan.style.display = 'inline-flex'; + userSpan.innerHTML = `👤 ${escapeHtml(this.user.name || this.user.email)}`; } - if (loginBtn) loginBtn.style.display = "none"; - if (registerBtn) { - registerBtn.innerText = this.user.isGuest ? "Konto anlegen" : "Abmelden"; - registerBtn.onclick = (e) => { - e.preventDefault(); - if (this.user.isGuest) { - window.openAuthModal("register"); - } else { + if (loginBtn) { + loginBtn.style.display = 'inline-flex'; + loginBtn.innerText = 'Abmelden'; + loginBtn.style.background = 'rgba(239, 68, 68, 0.15)'; + loginBtn.style.color = '#f87171'; + loginBtn.style.borderColor = 'rgba(239, 68, 68, 0.3)'; + loginBtn.onclick = () => { + if (confirm('Möchtest du dich wirklich abmelden?')) { this.logout(); } }; } - if (teacherLink) { - teacherLink.style.display = this.user.role === "teacher" ? "inline-flex" : "none"; + if (registerBtn) { + registerBtn.innerText = '💻 Zum Lernbereich'; + registerBtn.style.background = '#10b981'; + registerBtn.onclick = () => { + window.location.href = 'workspace.html?course=python'; + }; } - if (soloModeBadge) { - soloModeBadge.style.display = (this.user.role === "solo" || this.user.isGuest) ? "inline-block" : "none"; + if (heroPrimaryCta) { + heroPrimaryCta.innerHTML = '▶ Weiterlernen (Web-IDE)'; + heroPrimaryCta.onclick = (e) => { + window.location.href = 'workspace.html?course=python'; + }; + } + if (teacherLink) { + teacherLink.style.display = this.user.role === 'teacher' ? 'inline-flex' : 'none'; } } else { - if (userBadge) userBadge.style.display = "none"; - if (loginBtn) loginBtn.style.display = "inline-flex"; - if (registerBtn) { - registerBtn.innerText = "Konto erstellen"; - registerBtn.onclick = () => window.openAuthModal("register"); + if (userSpan) userSpan.style.display = 'none'; + if (loginBtn) { + loginBtn.style.display = 'inline-flex'; + loginBtn.innerText = '🔑 Anmelden'; + loginBtn.style.background = 'rgba(255,255,255,0.12)'; + loginBtn.style.color = 'white'; + loginBtn.style.borderColor = 'rgba(255,255,255,0.2)'; + loginBtn.onclick = () => window.openAuthModal('login', { allowClose: true }); } - if (teacherLink) teacherLink.style.display = "none"; - if (soloModeBadge) soloModeBadge.style.display = "inline-block"; + if (registerBtn) { + registerBtn.style.display = 'inline-flex'; + registerBtn.innerText = '✨ Kostenlos registrieren'; + registerBtn.style.background = '#0284c7'; + registerBtn.onclick = () => window.openAuthModal('register', { allowClose: true }); + } + if (heroPrimaryCta) { + heroPrimaryCta.innerHTML = '🚀 Jetzt kostenlos registrieren'; + heroPrimaryCta.onclick = (e) => { + e.preventDefault(); + window.openAuthModal('register', { allowClose: true }); + }; + } + if (teacherLink) teacherLink.style.display = 'none'; } + + // Aktualisiere Kurs-Karten Buttons auf der Startseite + document.querySelectorAll('.portal-card-action-btn').forEach(btn => { + const courseId = btn.getAttribute('data-course-id') || 'python'; + const targetUrl = btn.getAttribute('data-course-url') || `workspace.html?course=${courseId}`; + + if (loggedIn) { + btn.innerHTML = '▶ Kurs starten →'; + btn.classList.remove('locked'); + btn.onclick = () => { window.location.href = targetUrl; }; + } else { + btn.innerHTML = '🔒 Kostenlos freischalten'; + btn.classList.add('locked'); + btn.onclick = (e) => { + e.preventDefault(); + window.openAuthModal('register', { + allowClose: true, + title: '🔒 Kurszugang freischalten', + message: 'Erstelle einen kostenlosen Account in unter 30 Sekunden, um sofort vollen Zugriff auf diesen Kurs und alle 216 Module zu erhalten.' + }); + }; + } + }); } }; - // Auth Modal UI mit Single-User & Gast-Option - window.openAuthModal = function (mode = "login") { - let modal = document.getElementById("auth-modal-overlay"); - if (!modal) { - modal = document.createElement("div"); - modal.id = "auth-modal-overlay"; - modal.className = "certificate-overlay"; - modal.innerHTML = ` -
Speichere deinen Code, XP und Zertifikate dauerhaft in der Cloud.
+ // Auth Modal UI + window.openAuthModal = function (initialMode = 'register', options = {}) { + const allowClose = options.allowClose !== false; + let modal = document.getElementById('auth-modal-overlay'); + if (modal) modal.remove(); - -Lerne im eigenen Tempo – ohne Registrierung, ohne Klasse.
- -Praxisnahe Automatisierung: Streams & Redirection, awk/sed/jq Text Mining, Job-Control, Multithreading mit xargs/parallel, Systemd Services, Docker & CI/CD Pipelines.
+Terminal-Mastery für Sysadmins: I/O Streams, Pipes, Text-Mining mit jq, sed und awk, Parallelisierung (xargs/parallel), Docker-Scripting & BATS Unittests.
Objekt-orientiertes Scripting: .NET Pipeline-Architektur, PSCustomObject, CmdletBinding, Multithreading (-Parallel), REST APIs, Registry, Pester Tests & Cloud Engine.
+Modernes Microsoft & Multi-Platform Scripting: Die Objekt-Pipeline, Custom PSCustomObjects, REST-APIs, moderne Parallelisierung mit ForEach-Object -Parallel & Pester Unit Tests.
Vom SELECT bis zum Data Warehouse: Normalisierung (1NF–3NF), INNER/LEFT JOINs, Aggregate, Subqueries, CTEs, Window Functions (OVER/PARTITION BY) & ACID-Transaktionen.
+Vollständiges relationales Datenmanagement: Strikte Normalisierung (1NF–3NF), komplexe Multi-Table JOINs, Subqueries, Common Table Expressions (CTEs), Window Functions & ACID Transaktionen.
Moderne Web-Entwicklung: ES6+ Syntax, Klassen & OOP, Async/Await & Promises, DOM-Manipulation, Web Storage, ES-Module und statische Typisierung mit TypeScript Generics.
Zero-Cost Abstractions: Ownership & Borrowing, Lifetimes, Enums mit Option/Result, Traits, Smart Pointer (Box, Arc, Mutex), Fearless Concurrency & LRU-Cache Masterprojekt.
Entwickle hochskalierbare Cloud-Services mit Google Go: Goroutines, Channel-Pipelines, Mutex Synchronisation, Standard-HTTP-Server, Middleware & REST Microservice Master.
+ +Moderne Unternehmenssoftware: Top-Level Statements, Pattern Matching, Records, LINQ funktionale Datenabfragen, Asynchrones Task/await, xUnit Tests & Enterprise Order Engine.
Das Fundament der Systemprogrammierung: Pointer & dynamischer Speicher, RAII, Klassen & Rule of Five, Templates, Smart Pointer (unique_ptr, shared_ptr), STL & GoogleTest.
Modernes UI-Handwerk: Semantische Tags, Barrierefreiheit (ARIA/A11y), CSS Custom Properties, 1D-Layouts mit Flexbox, 2D-Layouts mit CSS Grid, Media Queries & Dark Mode.
Exakt nach der bundeseinheitlichen Ausbildungsordnung (AO 2020) gegliedert. Jedes Lernfeld besitzt ein eigenes Modul mit prüfungsrelevantem Grundlagenwissen, Cheat-Sheets und originalgetreuen IHK-Operatoren.
-Realistische Prüfungssimulation nach der neuen Ausbildungsordnung (AO 2020) für Fachinformatiker aller Fachrichtungen (FIAE, FISI, IT-SE, IT-SM).
Gemeinsame Kernqualifikationen aller Fachinformatiker: Verträge, Ergonomie, Subnetting, BSI-Schutzbedarf, SQL, IoT & Instandhaltung.
- AP1 Lehrplan → +Gemeinsame Kernqualifikationen aller Fachinformatiker: Verträge, Ergonomie, IPv4/IPv6 Subnetting, BSI-Schutzbedarf, relationales SQL & Trace-Tabellen.
+Fachrichtungsspezifisch: VLANs & Routing, Active Directory, RAID & SAN-Speicher, Virtualisierung, WiSo und 40h-Projektleitfaden.
- AP2 FISI Lehrplan → +Fachrichtungsspezifisch: VLANs & Routing, Active Directory GPO, RAID & SAN-Speicher, Virtualisierung, WiSo-Kompaktfragen & 40h-Projektleitfaden.
+Interaktiver Subnetting-Drill Generator, zeilenweiser Trace-Tabellen-Trainer und interaktives relationales SQL-Labor.
- Subnetting-Drill → +Blitzschnelles Rechnen: Subnetting-Drill Generator mit Live-Punktevergabe, schrittweise Trace-Tabellen-Simulation und relationale Abfrage-Validierung.
+Vergiss mühsame Compiler-Installationen oder Treiberprobleme. Das IT-Praxisportal bringt den Original-Code-Editor von Microsoft VS Code direkt in deinen Webbrowser – mit Autovervollständigung, Syntax-Highlighting, Unittests und automatischem Cloud-Save.
+Wissenschaftlich fundierte Didaktik, moderne Web-Technologien und zero Installation.
+Wissenschaftlich fundierte Didaktik, moderne Web-Technologien und zero lokale Installation.
Gleicher professioneller Editor wie in Microsoft VS Code: Syntax-Highlighting, Autocomplete, Shortcuts und automatische Tests direkt im Browser.
+Gleicher professioneller Editor wie in Microsoft VS Code: Syntax-Highlighting, Autocomplete, Shortcuts und automatisierte Tests direkt im Browser.
Echte Prüfungsbedingungen: 90 Minuten Countdown, 100 Punkte, dynamisch gemischte 150 IHK-Fragen und automatischer Notenrechner.
+Echte Prüfungsbedingungen: 90 Minuten Countdown, 100 Punkte, dynamisch gemischte 150 IHK-Fragen und automatischer Notenrechner nach IHK-Schlüssel.
Sammle XP für bestandene Tests, halte deinen täglichen Lern-Streak und drucke nach Abschluss verifizierbare PDF-Zertifikate aus.
+Sammle XP für bestandene Tests, halte deinen täglichen Lern-Streak und drucke nach Abschluss verifizierbare PDF-Zertifikate für Bewerbungen aus.
Erstelle Schulklassen mit Invite-Codes. Verfolge den Fortschritt deiner Schüler in der Live-Matrix und inspiziere eingereichten Code.
+Gehostet in Deutschland, kein Third-Party Tracking und verschlüsselte Passwörter. Speichere deinen Fortschritt sicher in der Cloud.
Generiere offizielle, personalisierte Abschlusszertifikate für deine Bewerbung oder Ausbildungsakte:
-Entwickelt von echten IHK-Dozenten und erfahrenen IT-Architekten.
Der ideale Begleiter vom ersten Ausbildungstag bis zur Abschlussprüfung. Schließe Wissenslücken in Berufsschul-Lernfeldern mit praxisnahem Code.
+Verständliche Didaktik ohne Vorwissen-Frust. Schritt-für-Schritt Subgoals bringen dich sicher durch technische Prüfungen und Job-Interviews.
+Organisiere ganze Klassen mit Invite-Codes. Behalte die Live-Lernmatrix deiner Azubis im Blick und inspiziere abgegebenen Code im Dozenten-Dashboard.
++ Erstelle jetzt deinen Account in weniger als 30 Sekunden. 100% kostenlos, keine Kreditkarte, kein Abonnement. +
+ +