lorem_ipsum/js/login.js

408 lines
13 KiB
JavaScript

/**
* Benutzer-Authentifizierung und Account-Verwaltung.
* Verwaltet den Login-Status über den LocalStorage, steuert die Anzeige im Header,
* verarbeitet Login- und Registrierungsformulare und ermöglicht das Löschen des Kontos.
*/
(function () {
// Der Schlüssel, unter dem die Benutzerdaten im LocalStorage abgelegt werden.
const AUTH_STORAGE_KEY = "loremIpsumAuth";
/**
* Liest die aktuellen Anmeldedaten aus dem LocalStorage des Browsers.
* @returns {Object|null} Ein Objekt mit {username, password} oder null, falls nicht angemeldet oder ungültig.
*/
function readAuth() {
const raw = localStorage.getItem(AUTH_STORAGE_KEY);
if (!raw) {
return null;
}
try {
const parsed = JSON.parse(raw);
// Validieren, dass die Pflichtfelder vorhanden sind
if (!parsed.username || !parsed.password) {
return null;
}
return parsed;
} catch {
// Fehler beim JSON-Parsen (z.B. manipulierte Daten) -> als nicht angemeldet behandeln
return null;
}
}
/**
* Speichert die Benutzerdaten verschlüsselt als JSON-String im LocalStorage.
* @param {string} username - Der Benutzername.
* @param {string} password - Das generierte/eingegebene Passwort.
*/
function saveAuth(username, password) {
localStorage.setItem(
AUTH_STORAGE_KEY,
JSON.stringify({ username: username, password: password }),
);
}
/**
* Entfernt die Anmeldedaten aus dem LocalStorage (Logout).
*/
function clearAuth() {
localStorage.removeItem(AUTH_STORAGE_KEY);
}
/**
* Aktualisiert die Anzeige des Benutzernamens im Header und den Login-Button.
*/
function updateHeaderUsername() {
const usernameDisplay = document.getElementById("username-display");
const navbarLogin = document.getElementById("navbar-login");
const auth = readAuth();
// Text im Header anpassen ("User: Guest" oder "User: <Name>")
if (usernameDisplay) {
usernameDisplay.textContent = auth ? "User: " + auth.username : "User: Guest";
}
// Text des Login-Links in der Navbar anpassen
if (navbarLogin) {
navbarLogin.textContent = auth ? auth.username : "Login / Registrieren";
}
}
/**
* Zeigt dem Benutzer Feedback (z. B. Fehlermeldungen) auf der Login-Seite an.
* @param {string} message - Die anzuzeigende Nachricht.
* @param {string} type - Der Bootstrap-Alert-Typ (danger, warning, success, info).
*/
function setFeedback(message, type) {
const feedback = document.getElementById("auth-feedback");
if (!feedback) {
return;
}
feedback.className = "alert alert-" + type;
feedback.textContent = message;
feedback.classList.remove("d-none"); // Alert einblenden
}
/**
* Aktualisiert den Zustand der Session-Box (Anzeige der aktuellen Anmeldung).
* Blendet die Forms aus bzw. ein und aktiviert/deaktiviert die Buttons.
*/
function updateSessionBox() {
const sessionText = document.getElementById("current-session-text");
const logoutButton = document.getElementById("logout-button");
const deleteAccountButton = document.getElementById("delete-account-button");
const currentSessionBox = document.getElementById("current-session-box");
const authFormsRow = document.getElementById("auth-forms-row");
if (!sessionText || !logoutButton || !deleteAccountButton || !currentSessionBox || !authFormsRow) {
return;
}
const auth = readAuth();
if (auth) {
// Wenn eingeloggt: Session-Info anzeigen und Buttons aktivieren
sessionText.textContent = "Eingeloggt als " + auth.username + ".";
logoutButton.disabled = false;
deleteAccountButton.disabled = false;
currentSessionBox.classList.remove("d-none");
authFormsRow.classList.remove("d-none");
} else {
// Wenn nicht eingeloggt: Info setzen und Session-Box verbergen
sessionText.textContent = "Nicht eingeloggt.";
logoutButton.disabled = true;
deleteAccountButton.disabled = true;
currentSessionBox.classList.add("d-none");
authFormsRow.classList.remove("d-none");
}
}
/**
* Hilfsfunktion zum Instanziieren des UserService.
* @returns {UserService|null} Eine UserService-Instanz oder null.
*/
function getUserService() {
if (!window.config || !window.UserService) {
return null;
}
return new window.UserService(window.config);
}
/**
* Behandelt das Absenden des Login-Formulars.
* Authentifiziert den Benutzer beim Backend und speichert die Session bei Erfolg.
* @param {Event} event - Das Submit-Event des Formulars.
*/
async function handleLoginSubmit(event) {
event.preventDefault();
const userService = getUserService();
if (!userService) {
setFeedback("User-Service konnte nicht geladen werden.", "danger");
return;
}
const usernameInput = document.getElementById("login-username");
const passwordInput = document.getElementById("login-password");
const submitButton = event.submitter; // Der geklickte Button
const username = usernameInput.value.trim();
const password = passwordInput.value.trim();
if (!username || !password) {
setFeedback("Bitte Username und Passwort eingeben.", "warning");
return;
}
// Button während der Anfrage sperren und Ladeindikator zeigen
if (submitButton) {
submitButton.disabled = true;
submitButton.textContent = "Einloggen...";
}
let result;
try {
result = await userService.getUser(username, password);
} catch (error) {
console.error("Login fehlgeschlagen:", error);
setFeedback("Login fehlgeschlagen: Backend ist nicht erreichbar.", "danger");
if (submitButton) {
submitButton.disabled = false;
submitButton.textContent = "Einloggen";
}
return;
}
// Button wieder freigeben
if (submitButton) {
submitButton.disabled = false;
submitButton.textContent = "Einloggen";
}
if (result.ok) {
// Login erfolgreich: Daten speichern, Ansicht aktualisieren und zur Startseite leiten
saveAuth(username, password);
setFeedback("Login erfolgreich.", "success");
updateSessionBox();
updateHeaderUsername();
// Falls das Navigationsmenü geöffnet ist, dessen Status aktualisieren
if (typeof window.updateMessagesNavState === "function") {
window.updateMessagesNavState();
}
// Kurze Verzögerung für visuelles Feedback vor dem Seitenwechsel
setTimeout(function() {
if (typeof window.loadPage === "function") {
window.loadPage("home", "nav-home");
}
}, 500);
return;
}
if (result.status === 401) {
setFeedback("Login fehlgeschlagen: Username oder Passwort ist falsch.", "danger");
return;
}
setFeedback("Login fehlgeschlagen (Status " + result.status + ").", "danger");
}
/**
* Behandelt das Absenden des Registrierungsformulars.
* Sendet den gewünschten Benutzernamen ans Backend, welches das Passwort generiert.
* Zeigt das Passwort anschließend in einem Bootstrap-Modal an, da es nur einmalig sichtbar ist.
* @param {Event} event - Das Submit-Event des Formulars.
*/
async function handleRegisterSubmit(event) {
event.preventDefault();
const userService = getUserService();
if (!userService) {
setFeedback("User-Service konnte nicht geladen werden.", "danger");
return;
}
const usernameInput = document.getElementById("register-username");
const username = usernameInput.value.trim();
if (!username) {
setFeedback("Bitte einen Username eingeben.", "warning");
return;
}
const result = await userService.postUser(username);
if (result.ok && result.body) {
// Registrierung erfolgreich: Antwortdaten auslesen
const createdName = result.body.name || username;
const createdPassword = result.body.password || "";
// Direkt einloggen mit den erhaltenen Daten
saveAuth(createdName, createdPassword);
updateSessionBox();
updateHeaderUsername();
if (typeof window.updateMessagesNavState === "function") {
window.updateMessagesNavState();
}
// Modal mit den generierten Zugangsdaten befüllen
const modalUsername = document.getElementById("modal-username");
const modalPassword = document.getElementById("modal-password");
if (modalUsername) modalUsername.textContent = createdName;
if (modalPassword) modalPassword.textContent = createdPassword;
// Das Passwort-Modal anzeigen
const passwordModalElement = document.getElementById("password-modal");
if (passwordModalElement) {
const passwordModal = new window.bootstrap.Modal(passwordModalElement);
// Nach Schließen des Modals automatisch auf die Startseite weiterleiten
passwordModalElement.addEventListener("hidden.bs.modal", function handleClose() {
if (typeof window.loadPage === "function") {
window.loadPage("home", "nav-home");
}
passwordModalElement.removeEventListener("hidden.bs.modal", handleClose);
});
passwordModal.show();
} else {
// Fallback, falls das Bootstrap-Modal im DOM nicht gefunden wurde
setFeedback(
"Account erstellt. Username: " + createdName + ", Passwort: " + createdPassword,
"success",
);
setTimeout(function() {
if (typeof window.loadPage === "function") {
window.loadPage("home", "nav-home");
}
}, 1500);
}
// Formularfelder für eventuelle spätere Logins vorausfüllen
const loginUsernameInput = document.getElementById("login-username");
const loginPasswordInput = document.getElementById("login-password");
if (loginUsernameInput && loginPasswordInput) {
loginUsernameInput.value = createdName;
loginPasswordInput.value = createdPassword;
}
return;
}
// Fehlerbehandlung
if (result.status === 400) {
const errorMessage = result.body && result.body.message
? result.body.message
: "Dieser Username existiert bereits.";
setFeedback(errorMessage, "danger");
return;
}
setFeedback(
"Account konnte nicht erstellt werden (Status " + result.status + ").",
"danger",
);
}
/**
* Loggt den aktuellen Benutzer aus (löscht Auth-Daten aus LocalStorage und aktualisiert UI).
*/
function handleLogout() {
clearAuth();
setFeedback("Du wurdest ausgeloggt.", "info");
updateSessionBox();
updateHeaderUsername();
if (typeof window.updateMessagesNavState === "function") {
window.updateMessagesNavState();
}
}
/**
* Behandelt das Löschen des eigenen Accounts.
* Fordert eine Sicherheitsbestätigung an, sendet die Löschanfrage ans Backend
* und führt bei Erfolg einen automatischen Logout aus.
*/
async function handleDeleteAccount() {
const auth = readAuth();
if (!auth) {
setFeedback("Du bist nicht eingeloggt.", "warning");
return;
}
// Sicherheitsabfrage im Browser
const wantsDelete = window.confirm(
"Möchtest du den Account \"" + auth.username + "\" wirklich löschen?",
);
if (!wantsDelete) {
return;
}
const userService = getUserService();
if (!userService) {
setFeedback("User-Service konnte nicht geladen werden.", "danger");
return;
}
const result = await userService.deleteUser(auth.username, auth.password);
if (result.ok) {
// Account erfolgreich gelöscht: Lokal ausloggen und Feedback geben
clearAuth();
updateSessionBox();
updateHeaderUsername();
if (typeof window.updateMessagesNavState === "function") {
window.updateMessagesNavState();
}
setFeedback("Account wurde gelöscht.", "success");
return;
}
if (result.status === 401) {
setFeedback("Account konnte nicht gelöscht werden: nicht autorisiert.", "danger");
return;
}
setFeedback(
"Account konnte nicht gelöscht werden (Status " + result.status + ").",
"danger",
);
}
/**
* Initialisiert die Login-Seite.
* Bindet alle Event-Listener an die Formulare und Buttons nach dem Laden des HTML-Inhalts.
*/
function initLoginPage() {
const loginForm = document.getElementById("login-form");
const registerForm = document.getElementById("register-form");
const logoutButton = document.getElementById("logout-button");
const deleteAccountButton = document.getElementById("delete-account-button");
if (!loginForm || !registerForm || !logoutButton || !deleteAccountButton) {
return;
}
loginForm.addEventListener("submit", handleLoginSubmit);
registerForm.addEventListener("submit", handleRegisterSubmit);
logoutButton.addEventListener("click", handleLogout);
deleteAccountButton.addEventListener("click", handleDeleteAccount);
// Initialen UI-Zustand herstellen
updateSessionBox();
updateHeaderUsername();
}
// Bindet die Initialisierungsfunktion an das globale Fensterobjekt für navigation.js
window.initLoginPage = initLoginPage;
// Stellt Authentifizierungsfunktionen global bereit, damit andere Module (z. B. play.js)
// prüfen können, ob und wer eingeloggt ist.
window.AppAuth = {
getAuth: readAuth,
clearAuth: clearAuth,
};
// Beim allerersten Laden der Anwendung direkt den Header anpassen
document.addEventListener("DOMContentLoaded", updateHeaderUsername);
})();