Rechtstexte, FinalFlux-Überarbeitung, neue Login-Optik, Konto-Löschung-Fix, BTTF-Farbrelaunch

- Rechtsseiten: Impressum-Platzhalter + echte Nutzungsbedingungen/AGB,
  Datenschutz (DSGVO) und Richtlinien; LegalController/ImpressumController,
  Footer app-weit verlinkt, Seiten als permitAll freigegeben.
- FinalFlux: 28 Themen / ~742 Fragen überarbeitet (plausible Distraktoren statt
  Dummy-Antworten, echte Erklärungen) + echte Lehrtexte je Seite;
  Netzwerktopologien mit Inline-SVG-Diagrammen (Stern/Bus/Ring/Mesh/Baum).
- Login/Registrierung als kombiniertes Dual-Panel (Slide-Overlay + Neon-Hover-
  Button), ausgelagert nach auth.css; Register-Duplikat -> /?registerError.
- Konto löschen repariert: @Transactional + Löschen von Spielständen,
  Achievements und Profilbild-Datei (DSGVO Art. 17). Impressum-Zurück-Link
  login-abhängig.
- App-weiter Farb-Relaunch auf "Back to the Future"-Palette: Feuer-Amber primär
  + Flux-Blau sekundär, warmer Glow; Dark/Light bleiben. Spiele-Feedback ohne
  Grün: richtig = Blau + Haken, falsch = Rot + Kreuz (neue Tokens
  --success/--danger/--secondary). Legacy-Cyan in style.css auf Variablen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
AliceDarkest
2026-06-29 19:08:05 +02:00
parent fc98e5aad1
commit 34b050503d
94 changed files with 6712 additions and 4220 deletions
@@ -19,7 +19,7 @@ public class SecurityConfig {
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/login", "/register", "/css/**", "/js/**", "/img/**").permitAll()
.requestMatchers("/", "/login", "/register", "/impressum", "/nutzungsbedingungen", "/datenschutz", "/richtlinien", "/css/**", "/js/**", "/img/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN") // NEU: Adminbereich absichern
.requestMatchers("/api/users/**").hasRole("ADMIN")
.anyRequest().authenticated()
@@ -0,0 +1,13 @@
package com.example.mindflux.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ImpressumController {
@GetMapping("/impressum")
public String impressum() {
return "impressum"; // verweist auf impressum.html in src/main/resources/templates
}
}
@@ -0,0 +1,27 @@
package com.example.mindflux.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* Liefert die statischen Rechts-/Informationsseiten aus dem Footer:
* AGB / Nutzungsbedingungen, Datenschutzerklärung und Community-Richtlinien.
*/
@Controller
public class LegalController {
@GetMapping("/nutzungsbedingungen")
public String nutzungsbedingungen() {
return "legal/nutzungsbedingungen";
}
@GetMapping("/datenschutz")
public String datenschutz() {
return "legal/datenschutz";
}
@GetMapping("/richtlinien")
public String richtlinien() {
return "legal/richtlinien";
}
}
@@ -179,6 +179,21 @@ public class ProfileController {
@PostMapping("/profile/delete")
public String deleteProfile(Principal principal, Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
String username = principal.getName();
// Profilbild-Datei von der Platte entfernen (DB-Daten löscht der Service in einer Transaktion).
User user = userService.findByUsername(username);
if (user != null) {
String profilePicture = user.getProfilePicture();
if (profilePicture != null && !profilePicture.trim().isEmpty()) {
try {
Path picturePath = Paths.get(System.getProperty("user.dir"), "uploads", "profiles", profilePicture);
Files.deleteIfExists(picturePath);
} catch (IOException e) {
System.err.println("Fehler beim Löschen des Profilbilds: " + e.getMessage());
}
}
}
userService.deleteByUsername(username);
new SecurityContextLogoutHandler().logout(request, response, authentication);
return "redirect:/?logout";
@@ -30,8 +30,8 @@ public class RegisterController {
// Prüfen, ob Benutzername schon existiert
if (userService.findByUsername(user.getUsername()) != null) {
model.addAttribute("error", "Benutzername bereits vergeben.");
return "register";
// Zurück zur kombinierten Login/Signup-Seite mit geöffnetem Signup-Panel
return "redirect:/?registerError";
}
// Passwort verschlüsseln
@@ -7,4 +7,7 @@ import java.util.List;
public interface AchievementRepository extends JpaRepository<Achievement, Long> {
List<Achievement> findByUsername(String username);
/** Alle Achievements eines Spielers löschen (z. B. beim Löschen des Kontos). */
void deleteByUsername(String username);
}
@@ -15,4 +15,7 @@ public interface GameScoreRepository extends JpaRepository<GameScore, Long> {
/** Wie oft ein Spieler ein Spiel abgeschlossen hat. */
long countByUsernameAndGame(String username, String game);
/** Alle Spielstände eines Spielers löschen (z. B. beim Löschen des Kontos). */
void deleteByUsername(String username);
}
@@ -1,9 +1,12 @@
package com.example.mindflux.service;
import com.example.mindflux.model.User;
import com.example.mindflux.repository.AchievementRepository;
import com.example.mindflux.repository.GameScoreRepository;
import com.example.mindflux.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserServiceImpl implements UserService {
@@ -11,6 +14,12 @@ public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private GameScoreRepository gameScoreRepository;
@Autowired
private AchievementRepository achievementRepository;
@Override
public User findByUsername(String username) {
return userRepository.findByUsername(username).orElse(null); // <-- Optional aufgelöst
@@ -22,7 +31,13 @@ public class UserServiceImpl implements UserService {
}
@Override
@Transactional
public void deleteByUsername(String username) {
// Zugehörige Daten mitlöschen (kein FK/Cascade -> manuell), damit keine
// verwaisten Datensätze zurückbleiben und ein neuer gleichnamiger Account
// keine Alt-Daten erbt.
gameScoreRepository.deleteByUsername(username);
achievementRepository.deleteByUsername(username);
userRepository.deleteByUsername(username);
}
}