AP6 DSGVO: Footer-Rollout, Datenschutz/Impressum, Datenexport, NeuralTest-Entfernung

- Footer-Fragment (fragments/layout :: footer) ueberall ausgerollt: 24 duplizierte
  Inline-Footer ersetzt, 11 bisher footerlose Seiten (Fehler-/Auth-Seiten + 8
  Spielseiten) ergaenzt -> Rechtslinks von jeder Seite erreichbar. Support/Feedback
  von href="#" auf mailto-Platzhalter umgestellt.
- Datenschutzerklaerung vervollstaendigt: NeuralNet-Messaging als Verarbeitungs-
  taetigkeit (Inhalte, Sender/Empfaenger, Lesebestaetigung, Loeschung via Kaskade),
  E-Mail-Token (SHA-256, 24h) + SMTP ueber mail.sysnaptik.de, konkrete Speicher-
  fristen, korrekte Drittland-/CDN-Aussage.
- Impressum strukturiert: Pflichtfelder nach § 5 DDG (Name, Anschrift, Vertretung,
  Kontakt, MStV) als auffaellige Platzhalter (--warn).
- Datenexport (Art. 20): GET /profile/export -> ProfileExportService sammelt
  Stammdaten, Scores, Achievements und NeuralNet-Daten (ohne Passwort) als
  JSON-Download; drei Repo-Query-Methoden ergaenzt.
- V22-Migration: Demo-User NeuralTest entfernt (NeuralNet-Daten per Kaskade).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WFxcjtc9HNP4VECT6Ur1tE
This commit is contained in:
AliceDarkest
2026-07-21 00:37:38 +02:00
parent 983e57cd8d
commit 1bfbd53062
42 changed files with 238 additions and 197 deletions
@@ -19,8 +19,13 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import java.security.Principal; import java.security.Principal;
import java.util.List; import java.util.List;
import java.util.Map;
import com.example.mindflux.dto.WeeklyActivity; import com.example.mindflux.dto.WeeklyActivity;
import com.example.mindflux.service.AvatarService; import com.example.mindflux.service.AvatarService;
import com.example.mindflux.service.ProfileExportService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@Controller @Controller
public class ProfileController { public class ProfileController {
@@ -39,6 +44,9 @@ public class ProfileController {
@Autowired @Autowired
private AvatarService avatarService; private AvatarService avatarService;
@Autowired
private ProfileExportService profileExportService;
@GetMapping("/profile") @GetMapping("/profile")
public String profile(Model model, Principal principal, @RequestParam(value = "success", required = false) Boolean success) { public String profile(Model model, Principal principal, @RequestParam(value = "success", required = false) Boolean success) {
String username = principal.getName(); String username = principal.getName();
@@ -73,6 +81,16 @@ public class ProfileController {
return "settings"; return "settings";
} }
/** DSGVO-Datenexport (Art. 20): alle Kontodaten als JSON-Download. */
@GetMapping("/profile/export")
public ResponseEntity<Map<String, Object>> exportData(Principal principal) {
Map<String, Object> data = profileExportService.export(principal.getName());
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"mindflux-datenexport.json\"")
.contentType(MediaType.APPLICATION_JSON)
.body(data);
}
@GetMapping("/profile/knowledge") @GetMapping("/profile/knowledge")
public String knowledgeCenter(Model model, Principal principal) { public String knowledgeCenter(Model model, Principal principal) {
String username = principal.getName(); String username = principal.getName();
@@ -27,6 +27,9 @@ public interface GameScoreRepository extends JpaRepository<GameScore, Long> {
/** Alle Spielstände eines Spielers löschen (z. B. beim Löschen des Kontos). */ /** Alle Spielstände eines Spielers löschen (z. B. beim Löschen des Kontos). */
void deleteByUsername(String username); void deleteByUsername(String username);
/** Alle Spielstände eines Spielers (für den DSGVO-Datenexport). */
List<GameScore> findByUsernameOrderByPlayedAtDesc(String username);
/** Gesamtsumme aller Scores eines Spielers (XP-Basis). */ /** Gesamtsumme aller Scores eines Spielers (XP-Basis). */
@Query("select coalesce(sum(g.score), 0) from GameScore g where g.username = :username") @Query("select coalesce(sum(g.score), 0) from GameScore g where g.username = :username")
long sumScoreByUsername(@Param("username") String username); long sumScoreByUsername(@Param("username") String username);
@@ -11,4 +11,6 @@ public interface NeuralNetConnectionRepository extends JpaRepository<NeuralNetCo
List<NeuralNetConnection> acceptedFor(@Param("username") String username); List<NeuralNetConnection> acceptedFor(@Param("username") String username);
@Query("select c from NeuralNetConnection c where c.status='ACCEPTED' and ((c.requester=:a and c.addressee=:b) or (c.requester=:b and c.addressee=:a))") @Query("select c from NeuralNetConnection c where c.status='ACCEPTED' and ((c.requester=:a and c.addressee=:b) or (c.requester=:b and c.addressee=:a))")
Optional<NeuralNetConnection> acceptedBetween(@Param("a") String a, @Param("b") String b); Optional<NeuralNetConnection> acceptedBetween(@Param("a") String a, @Param("b") String b);
@Query("select c from NeuralNetConnection c where c.requester=:u or c.addressee=:u order by c.createdAt asc")
List<NeuralNetConnection> findAllInvolving(@Param("u") String u);
} }
@@ -7,4 +7,6 @@ public interface NeuralNetMessageRepository extends JpaRepository<NeuralNetMessa
@Query("select m from NeuralNetMessage m where (m.sender=:a and m.recipient=:b) or (m.sender=:b and m.recipient=:a) order by m.sentAt asc") @Query("select m from NeuralNetMessage m where (m.sender=:a and m.recipient=:b) or (m.sender=:b and m.recipient=:a) order by m.sentAt asc")
List<NeuralNetMessage> conversation(@Param("a") String a, @Param("b") String b); List<NeuralNetMessage> conversation(@Param("a") String a, @Param("b") String b);
long countByRecipientAndReadAtIsNull(String recipient); long countByRecipientAndReadAtIsNull(String recipient);
@Query("select m from NeuralNetMessage m where m.sender=:u or m.recipient=:u order by m.sentAt asc")
List<NeuralNetMessage> findAllInvolving(@Param("u") String u);
} }
@@ -0,0 +1,114 @@
package com.example.mindflux.service;
import com.example.mindflux.model.Achievement;
import com.example.mindflux.model.GameScore;
import com.example.mindflux.model.NeuralNetConnection;
import com.example.mindflux.model.NeuralNetMessage;
import com.example.mindflux.model.User;
import com.example.mindflux.repository.AchievementRepository;
import com.example.mindflux.repository.GameScoreRepository;
import com.example.mindflux.repository.NeuralNetConnectionRepository;
import com.example.mindflux.repository.NeuralNetMessageRepository;
import com.example.mindflux.repository.NeuralNetPrivacyRepository;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Sammelt alle personenbezogenen Daten eines Kontos für den DSGVO-Datenexport
* (Art. 20 – Datenübertragbarkeit). Bewusst OHNE das Passwort-Hash; nur Daten,
* die die betroffene Person selbst erzeugt hat.
*/
@Service
public class ProfileExportService {
private final UserService userService;
private final GameScoreRepository scores;
private final AchievementRepository achievements;
private final NeuralNetPrivacyRepository privacy;
private final NeuralNetConnectionRepository connections;
private final NeuralNetMessageRepository messages;
public ProfileExportService(UserService userService, GameScoreRepository scores,
AchievementRepository achievements, NeuralNetPrivacyRepository privacy,
NeuralNetConnectionRepository connections, NeuralNetMessageRepository messages) {
this.userService = userService;
this.scores = scores;
this.achievements = achievements;
this.privacy = privacy;
this.connections = connections;
this.messages = messages;
}
public Map<String, Object> export(String username) {
Map<String, Object> out = new LinkedHashMap<>();
out.put("exportiertAm", LocalDateTime.now().toString());
out.put("hinweis", "DSGVO-Datenexport (Art. 20). Enthält alle zu diesem Konto gespeicherten "
+ "personenbezogenen Daten außer dem Passwort-Hash.");
User user = userService.findByUsername(username);
Map<String, Object> account = new LinkedHashMap<>();
if (user != null) {
account.put("benutzername", user.getUsername());
account.put("email", user.getEmail());
account.put("rolle", user.getRole());
account.put("emailBestaetigt", user.isEmailVerified());
account.put("hatProfilbild", user.getProfilePicture() != null && !user.getProfilePicture().isBlank());
}
out.put("konto", account);
List<Map<String, Object>> scoreList = new ArrayList<>();
for (GameScore s : scores.findByUsernameOrderByPlayedAtDesc(username)) {
Map<String, Object> m = new LinkedHashMap<>();
m.put("spiel", s.getGame());
m.put("deck", s.getDeck());
m.put("punkte", s.getScore());
m.put("zuege", s.getMoves());
m.put("dauerSekunden", s.getDurationSeconds());
m.put("gespieltAm", s.getPlayedAt() != null ? s.getPlayedAt().toString() : null);
scoreList.add(m);
}
out.put("spielstaende", scoreList);
List<Map<String, Object>> achievementList = new ArrayList<>();
for (Achievement a : achievements.findByUsername(username)) {
Map<String, Object> m = new LinkedHashMap<>();
m.put("titel", a.getTitle());
m.put("beschreibung", a.getDescription());
m.put("erreichtAm", a.getDateAchieved() != null ? a.getDateAchieved().toString() : null);
achievementList.add(m);
}
out.put("erfolge", achievementList);
Map<String, Object> neural = new LinkedHashMap<>();
neural.put("nachrichtenAktiviert", privacy.findById(username).map(p -> p.isEnabled()).orElse(false));
List<Map<String, Object>> connList = new ArrayList<>();
for (NeuralNetConnection c : connections.findAllInvolving(username)) {
Map<String, Object> m = new LinkedHashMap<>();
m.put("anfragender", c.getRequester());
m.put("empfaenger", c.getAddressee());
m.put("status", c.getStatus());
m.put("erstelltAm", c.getCreatedAt() != null ? c.getCreatedAt().toString() : null);
connList.add(m);
}
neural.put("verbindungen", connList);
List<Map<String, Object>> msgList = new ArrayList<>();
for (NeuralNetMessage msg : messages.findAllInvolving(username)) {
Map<String, Object> m = new LinkedHashMap<>();
m.put("von", msg.getSender());
m.put("an", msg.getRecipient());
m.put("text", msg.getBody());
m.put("gesendetAm", msg.getSentAt() != null ? msg.getSentAt().toString() : null);
m.put("gelesen", msg.getReadAt() != null);
msgList.add(m);
}
neural.put("nachrichten", msgList);
out.put("neuralNet", neural);
return out;
}
}
@@ -0,0 +1,14 @@
-- V22: Entfernt das Demo-/Testkonto "NeuralTest" (per V17 fuer den NeuralNet-Flow
-- geseedet) aus produktiven Datenbanken. Ein klar erkennbares Testkonto gehoert
-- nicht in Produktion.
--
-- Die NeuralNet-Tabellen (neuralnet_privacy, neuralnet_connections, neuralnet_messages)
-- referenzieren users(username) mit ON DELETE CASCADE und werden beim Loeschen des
-- Kontos automatisch mitgeraeumt. game_scores hat keinen Fremdschluessel, daher wird
-- ein evtl. vorhandener Spielstand des Demo-Kontos defensiv vorab entfernt.
--
-- Wird das Konto fuer die lokale Entwicklung gebraucht, sollte es ueber einen
-- @Profile("dev")-gebundenen Runner neu angelegt werden, nicht per Migration.
DELETE FROM game_scores WHERE username = 'NeuralTest';
DELETE FROM users WHERE username = 'NeuralTest';
@@ -60,14 +60,7 @@
</div> </div>
<!-- Footer --> <!-- Footer -->
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -59,14 +59,7 @@
</main> </main>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</div> </div>
@@ -99,10 +99,6 @@
</div> </div>
</main> </main>
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a><a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a><a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a><a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -87,10 +87,6 @@
<p th:if="${games.isEmpty()}" class="empty">Noch keine Spiele. Lege oben das erste an.</p> <p th:if="${games.isEmpty()}" class="empty">Noch keine Spiele. Lege oben das erste an.</p>
</main> </main>
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a><a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a><a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a><a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -122,14 +122,7 @@
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -164,14 +164,7 @@
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -127,14 +127,7 @@
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -126,14 +126,7 @@
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -83,14 +83,7 @@
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -55,14 +55,7 @@
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -66,14 +66,7 @@
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
+2 -1
View File
@@ -1 +1,2 @@
<!doctype html><html lang="de" data-theme="dark"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>BugHunter · SysnaptiK Neural</title><link rel="stylesheet" href="/css/theme.css?v=4"><link rel="stylesheet" href="/css/bughunter.css?v=3"><script src="/js/theme.js?v=5"></script></head><body class="bh-body"><main class="bh-app"><header class="bh-top"><div style="display:flex;align-items:center;gap:.65rem"><a href="/dashboard" class="bh-back">← Dashboard</a><a href="/bughunter" class="bh-btn">AKTENSCHRANK</a><button class="mf-theme" type="button" aria-label="Theme umschalten"></button></div><div class="bh-brand"><b>BUGHUNTER</b><span>DEBUG-FALLAKTEN</span></div><span class="mf-a11y-host"></span></header><section id="bhView" class="bh-view"><div class="bh-loading">FALLAKTEN WERDEN GEÖFFNET …</div></section></main><script src="/js/bughunter.js?v=5"></script></body></html> <!doctype html><html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>BugHunter · SysnaptiK Neural</title><link rel="stylesheet" href="/css/theme.css?v=4"><link rel="stylesheet" href="/css/bughunter.css?v=3"><script src="/js/theme.js?v=5"></script></head><body class="bh-body"><main class="bh-app"><header class="bh-top"><div style="display:flex;align-items:center;gap:.65rem"><a href="/dashboard" class="bh-back">← Dashboard</a><a href="/bughunter" class="bh-btn">AKTENSCHRANK</a><button class="mf-theme" type="button" aria-label="Theme umschalten"></button></div><div class="bh-brand"><b>BUGHUNTER</b><span>DEBUG-FALLAKTEN</span></div><span class="mf-a11y-host"></span></header><section id="bhView" class="bh-view"><div class="bh-loading">FALLAKTEN WERDEN GEÖFFNET …</div></section></main><script src="/js/bughunter.js?v=5"></script> <footer th:replace="~{fragments/layout :: footer}"></footer>
</body></html>
+2 -1
View File
@@ -1 +1,2 @@
<!doctype html><html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SystemEscape · SysnaptiK Neural</title><link rel="stylesheet" href="/css/theme.css?v=4"><link rel="stylesheet" href="/css/codeescape.css?v=2"><link rel="stylesheet" href="/css/codeescape-guide.css?v=3"><link rel="stylesheet" href="/css/codeescape-global.css?v=1"><script src="/js/theme.js?v=5"></script></head><body><main class="esc-app"><header class="esc-top"><a href="/dashboard" class="esc-back">← Dashboard</a><div class="esc-brand"><b>SYSTEMESCAPE</b><span>FACILITY ACCESS PROTOCOL</span></div><div class="esc-global-tools"><span class="mf-a11y-host"></span><div class="esc-timer" id="escTimer">--:--</div></div></header><section id="escView" class="esc-view"><div class="esc-loading">FACILITY WIRD INITIALISIERT …</div></section></main><script src="/js/codeescape.js?v=2"></script></body></html> <!doctype html><html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SystemEscape · SysnaptiK Neural</title><link rel="stylesheet" href="/css/theme.css?v=4"><link rel="stylesheet" href="/css/codeescape.css?v=2"><link rel="stylesheet" href="/css/codeescape-guide.css?v=3"><link rel="stylesheet" href="/css/codeescape-global.css?v=1"><script src="/js/theme.js?v=5"></script></head><body><main class="esc-app"><header class="esc-top"><a href="/dashboard" class="esc-back">← Dashboard</a><div class="esc-brand"><b>SYSTEMESCAPE</b><span>FACILITY ACCESS PROTOCOL</span></div><div class="esc-global-tools"><span class="mf-a11y-host"></span><div class="esc-timer" id="escTimer">--:--</div></div></header><section id="escView" class="esc-view"><div class="esc-loading">FACILITY WIRD INITIALISIERT …</div></section></main><script src="/js/codeescape.js?v=2"></script> <footer th:replace="~{fragments/layout :: footer}"></footer>
</body></html>
+1 -8
View File
@@ -130,14 +130,7 @@
</aside> </aside>
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
<script th:inline="none"> <script th:inline="none">
const GI = { const GI = {
@@ -83,5 +83,6 @@
</div> </div>
<script src="/js/devlabgame.js?v=3" defer></script> <script src="/js/devlabgame.js?v=3" defer></script>
<footer th:replace="~{fragments/layout :: footer}"></footer>
</body> </body>
</html> </html>
+1 -8
View File
@@ -110,14 +110,7 @@
</main> </main>
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
<script src="/js/devlab.js?v=4" defer></script> <script src="/js/devlab.js?v=4" defer></script>
</body> </body>
+1
View File
@@ -27,5 +27,6 @@
</div> </div>
</div> </div>
<footer th:replace="~{fragments/layout :: footer}"></footer>
</body> </body>
</html> </html>
+2 -9
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de" data-theme="dark"> <html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>FinalProtocol - SysnaptiK Neural</title> <title>FinalProtocol - SysnaptiK Neural</title>
@@ -123,14 +123,7 @@
</section> </section>
</main> </main>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -25,5 +25,6 @@
<p style="margin-top:1rem"><a href="/">Zur Anmeldung</a></p> <p style="margin-top:1rem"><a href="/">Zur Anmeldung</a></p>
</section> </section>
</main> </main>
<footer th:replace="~{fragments/layout :: footer}"></footer>
</body> </body>
</html> </html>
@@ -85,8 +85,8 @@
<!-- ===== Footer ===== --> <!-- ===== Footer ===== -->
<footer class="mf-footer" th:fragment="footer"> <footer class="mf-footer" th:fragment="footer">
<a href="#">Support</a> <a href="mailto:support@sysnaptik.de">Support</a>
<a href="#">Feedback</a> <a href="mailto:feedback@sysnaptik.de">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a> <a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a> <a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a> <a href="/datenschutz">Datenschutz</a>
+23 -18
View File
@@ -17,6 +17,10 @@
border-radius:10px; text-align:left; margin-bottom:1.6rem; } border-radius:10px; text-align:left; margin-bottom:1.6rem; }
.mf-cardbox .meta { color:var(--muted); font-size:.85rem; line-height:1.6; text-align:left; margin-bottom:1.6rem; } .mf-cardbox .meta { color:var(--muted); font-size:.85rem; line-height:1.6; text-align:left; margin-bottom:1.6rem; }
.mf-cardbox .mf-back { display:inline-block; } .mf-cardbox .mf-back { display:inline-block; }
.mf-legal { text-align:left; margin:0 0 1.6rem; }
.mf-legal dt { color:var(--heading); font-weight:700; font-size:.78rem; text-transform:uppercase; letter-spacing:.06em; margin-top:1rem; }
.mf-legal dd { color:var(--text); margin:.25rem 0 0; font-size:.92rem; line-height:1.5; }
.mf-legal dd.ph { color:var(--warn); font-style:italic; border:1px dashed color-mix(in srgb,var(--warn) 55%,var(--border)); background:color-mix(in srgb,var(--warn) 10%,transparent); border-radius:8px; padding:.45rem .65rem; }
</style> </style>
</head> </head>
<body> <body>
@@ -27,34 +31,35 @@
<div class="mf-cardbox"> <div class="mf-cardbox">
<div class="mf-doc">📄</div> <div class="mf-doc">📄</div>
<h1>Impressum</h1> <h1>Impressum</h1>
<p class="slogan">Rechtliche Angaben – Platzhalter</p> <p class="slogan">Rechtliche Angaben gemäß § 5 DDG</p>
<p class="notice"> <p class="notice">
SysnaptiK Neural befindet sich aktuell <strong>in aktiver Entwicklung</strong> und ist <strong>Hinweis:</strong> SysnaptiK Neural befindet sich in aktiver Entwicklung. Die folgenden
<strong>nicht für die öffentliche Nutzung</strong> bestimmt. Die hier dargestellten Pflichtangaben sind bewusst als <strong>Platzhalter</strong> markiert und werden vor der
Inhalte sind ein Platzhalter und dienen ausschließlich internen Test- und öffentlichen Veröffentlichung mit den tatsächlichen Daten des Anbieters ausgefüllt.
Entwicklungszwecken.
</p> </p>
<p class="meta"> <dl class="mf-legal">
Ein vollständiges Impressum gemäß den gesetzlichen Anforderungen wird vor einer <dt>Diensteanbieter</dt>
öffentlichen Veröffentlichung ergänzt.<br> <dd class="ph">[ Name bzw. Firmenbezeichnung ]</dd>
© 2026 Sysnaptik – Alle Rechte vorbehalten. <dt>Anschrift</dt>
</p> <dd class="ph">[ Straße und Hausnummer ]<br>[ PLZ und Ort ]<br>[ Land ]</dd>
<dt>Vertreten durch</dt>
<dd class="ph">[ vertretungsberechtigte Person ]</dd>
<dt>Kontakt</dt>
<dd class="ph">E-Mail: [ kontakt@sysnaptik.de ]</dd>
<dt>Verantwortlich i. S. d. § 18 Abs. 2 MStV</dt>
<dd class="ph">[ Name und Anschrift der verantwortlichen Person ]</dd>
</dl>
<p class="meta">© 2026 Sysnaptik – Alle Rechte vorbehalten.</p>
<a th:href="@{/dashboard}" class="mf-back" sec:authorize="isAuthenticated()">⟵ Zurück zum Dashboard</a> <a th:href="@{/dashboard}" class="mf-back" sec:authorize="isAuthenticated()">⟵ Zurück zum Dashboard</a>
<a th:href="@{/}" class="mf-back" sec:authorize="!isAuthenticated()">⟵ Zurück</a> <a th:href="@{/}" class="mf-back" sec:authorize="!isAuthenticated()">⟵ Zurück</a>
</div> </div>
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
+1 -8
View File
@@ -133,14 +133,7 @@
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</div> </div>
</div> </div>
+1 -1
View File
@@ -38,6 +38,6 @@
</ul> </ul>
</section> </section>
</main> </main>
<footer class="mf-footer"><a href="#">Support</a><a href="#">Feedback</a><a href="/nutzungsbedingungen">Nutzungsbedingungen</a><a href="/richtlinien">Richtlinien</a><a href="/datenschutz">Datenschutz</a><a href="/impressum">Impressum</a></footer> <footer th:replace="~{fragments/layout :: footer}"></footer>
</body> </body>
</html> </html>
@@ -62,6 +62,9 @@
<tr><td>Profil</td><td>Optional hochgeladenes Profilbild (serverseitig auf max. 400 px skaliert, als JPEG gespeichert)</td></tr> <tr><td>Profil</td><td>Optional hochgeladenes Profilbild (serverseitig auf max. 400 px skaliert, als JPEG gespeichert)</td></tr>
<tr><td>Nutzungs-/Lerndaten</td><td>Spielstände (Spiel, Deck, Punktzahl, Züge, Dauer, Zeitpunkt) sowie erreichte Erfolge/Achievements</td></tr> <tr><td>Nutzungs-/Lerndaten</td><td>Spielstände (Spiel, Deck, Punktzahl, Züge, Dauer, Zeitpunkt) sowie erreichte Erfolge/Achievements</td></tr>
<tr><td>Technische Daten</td><td>Sitzungs-Cookie zur Anmeldung, CSRF-Token; serverseitige Protokolldaten (z. B. IP-Adresse, Zeitpunkt, abgerufene Ressource) im Rahmen des technischen Betriebs</td></tr> <tr><td>Technische Daten</td><td>Sitzungs-Cookie zur Anmeldung, CSRF-Token; serverseitige Protokolldaten (z. B. IP-Adresse, Zeitpunkt, abgerufene Ressource) im Rahmen des technischen Betriebs</td></tr>
<tr><td>NeuralNet-Nachrichten (nur bei Aktivierung)</td><td>Verbindungsanfragen (anfragende und empfangende Person, Status, Zeitpunkt) sowie Nachrichten (Absender, Empfänger, Nachrichteninhalt, Sende- und Lesezeitpunkt/Lesebestätigung)</td></tr>
<tr><td>E-Mail-Bestätigung / Passwort-Reset</td><td>Bestätigungs- bzw. Reset-Token, ausschließlich als SHA-256-Hash gespeichert und 24 Stunden gültig, zur Verifizierung der E-Mail-Adresse bzw. zum Zurücksetzen des Passworts</td></tr>
<tr><td>Anmeldeschutz</td><td>Fehlgeschlagene Anmeldeversuche (Benutzername, Zeitpunkt) zum Schutz vor automatisierten Anmeldeangriffen (Brute Force)</td></tr>
</table> </table>
<h2>4. Zwecke und Rechtsgrundlagen</h2> <h2>4. Zwecke und Rechtsgrundlagen</h2>
@@ -69,6 +72,8 @@
<li><strong>Bereitstellung des Dienstes und Kontoverwaltung</strong> (Registrierung, Anmeldung, Lernfunktionen, Spielstände, Achievements) – Art. 6 Abs. 1 lit. b DSGVO (Vertrag/Nutzungsverhältnis).</li> <li><strong>Bereitstellung des Dienstes und Kontoverwaltung</strong> (Registrierung, Anmeldung, Lernfunktionen, Spielstände, Achievements) – Art. 6 Abs. 1 lit. b DSGVO (Vertrag/Nutzungsverhältnis).</li>
<li><strong>Technischer Betrieb, Sicherheit und Stabilität</strong> (Protokolldaten, Schutz vor Missbrauch) – Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse).</li> <li><strong>Technischer Betrieb, Sicherheit und Stabilität</strong> (Protokolldaten, Schutz vor Missbrauch) – Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse).</li>
<li><strong>Profilbild</strong> – auf Grundlage der freiwilligen Bereitstellung durch die Nutzer:in.</li> <li><strong>Profilbild</strong> – auf Grundlage der freiwilligen Bereitstellung durch die Nutzer:in.</li>
<li><strong>NeuralNet-Community-Funktion</strong> (Verbindungsanfragen und Nachrichten) – nur nach ausdrücklicher Aktivierung (Opt-in, standardmäßig deaktiviert), Art. 6 Abs. 1 lit. a und b DSGVO. Die Funktion lässt sich jederzeit deaktivieren; beim Deaktivieren bzw. beim Löschen des Kontos werden die zugehörigen Nachrichten und Verbindungen per Datenbank-Kaskade mitgelöscht.</li>
<li><strong>E-Mail-Verifizierung und Passwort-Zurücksetzung</strong> (Versand von Bestätigungs- und Reset-Links) – Art. 6 Abs. 1 lit. b und f DSGVO.</li>
</ul> </ul>
<h2>5. Cookies</h2> <h2>5. Cookies</h2>
@@ -79,12 +84,17 @@
</p> </p>
<h2>6. Speicherdauer</h2> <h2>6. Speicherdauer</h2>
<p>Wir speichern personenbezogene Daten nur so lange, wie es für den jeweiligen Zweck erforderlich ist:</p>
<ul>
<li><strong>Kontodaten, Spielstände, Erfolge:</strong> solange das Nutzerkonto besteht; werden bei Kontolöschung entfernt.</li>
<li><strong>NeuralNet-Nachrichten und -Verbindungen:</strong> bis zur Löschung durch die Nutzer:in bzw. bis zur Kontolöschung (dann per Kaskade automatisch).</li>
<li><strong>Bestätigungs- und Passwort-Reset-Token:</strong> 24 Stunden; danach automatisch ungültig und nicht mehr verwendbar.</li>
<li><strong>Fehlgeschlagene Anmeldeversuche:</strong> nur kurzfristig für den Brute-Force-Schutz, anschließend verworfen.</li>
<li><strong>Sitzungs-Cookie:</strong> verliert mit Ende der Sitzung bzw. der Abmeldung seine Gültigkeit.</li>
</ul>
<p> <p>
Kontobezogene Daten werden gespeichert, solange das Nutzerkonto besteht. Nach Löschung des Kontos Gesetzliche Aufbewahrungspflichten bleiben unberührt. Da sich der Dienst in der Entwicklung befindet,
werden die zugehörigen personenbezogenen Daten gelöscht, soweit keine gesetzlichen können Daten zudem im Rahmen von Tests zurückgesetzt werden.
Aufbewahrungspflichten entgegenstehen. Das Sitzungs-Cookie verliert mit dem Ende der Sitzung bzw.
der Abmeldung seine Gültigkeit. Da sich der Dienst in der Entwicklung befindet, können Daten zudem
im Rahmen von Tests zurückgesetzt werden.
</p> </p>
<h2>6a. Lokale Einstellungen, Barrierefreiheit und NeuralPause</h2> <h2>6a. Lokale Einstellungen, Barrierefreiheit und NeuralPause</h2>
@@ -102,9 +112,12 @@
<h2>7. Empfänger und Hosting</h2> <h2>7. Empfänger und Hosting</h2>
<p> <p>
Die Daten werden auf einer vom Anbieter betriebenen Server-Infrastruktur verarbeitet und nicht zu Die Daten werden auf einer vom Anbieter betriebenen Server-Infrastruktur verarbeitet und nicht zu
werblichen Zwecken an Dritte weitergegeben. Eine Übermittlung erfolgt nur, soweit dies für den werblichen Zwecken an Dritte weitergegeben. Für den Versand von Bestätigungs- und Passwort-Reset-E-Mails
Betrieb technisch erforderlich oder gesetzlich vorgeschrieben ist. Eine Datenübermittlung in wird der Mailserver <code>mail.sysnaptik.de</code> genutzt; dabei werden E-Mail-Adresse und der jeweilige
Drittländer außerhalb der EU/des EWR findet nicht statt. Link übermittelt. Alle statischen Inhalte (einschließlich der Diagramm-Bibliothek) werden ausschließlich
von der eigenen Infrastruktur ausgeliefert – es werden keine externen Content-Delivery-Networks oder
Drittanbieter-Skripte eingebunden. Eine Datenübermittlung in Drittländer außerhalb der EU/des EWR findet
nicht statt.
</p> </p>
<h2>8. Datensicherheit</h2> <h2>8. Datensicherheit</h2>
@@ -134,14 +147,7 @@
</p> </p>
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -138,14 +138,7 @@
</p> </p>
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -104,14 +104,7 @@
</p> </p>
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -65,5 +65,6 @@
</div> </div>
<script src="/js/mindmatch.js?v=3" defer></script> <script src="/js/mindmatch.js?v=3" defer></script>
<footer th:replace="~{fragments/layout :: footer}"></footer>
</body> </body>
</html> </html>
+2 -1
View File
@@ -1 +1,2 @@
<!doctype html><html lang="de" data-theme="dark"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>NetWorker · SysnaptiK Neural</title><link rel="stylesheet" href="/css/theme.css?v=4"><link rel="stylesheet" href="/css/networker.css?v=2"><link rel="stylesheet" href="/css/networker-guide.css?v=1"><script src="/js/theme.js?v=5"></script></head><body class="nw-body"><main class="nw-app"><header class="nw-top"><div style="display:flex;align-items:center;gap:.65rem"><a href="/dashboard" class="nw-back">← Dashboard</a><a href="/networker" class="nw-btn">NETZPLÄNE</a><button class="mf-theme" type="button" aria-label="Theme umschalten"></button></div><div class="nw-brand"><b>NetWorker</b><span>NETZPLAN · DIAGNOSE</span></div><span class="mf-a11y-host"></span></header><section id="nwView" class="nw-view"><div class="nw-loading">NETZWERKPLÄNE WERDEN KALIBRIERT …</div></section></main><script src="/js/networker.js?v=1"></script><script src="/js/networker-guide.js?v=1"></script></body></html> <!doctype html><html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>NetWorker · SysnaptiK Neural</title><link rel="stylesheet" href="/css/theme.css?v=4"><link rel="stylesheet" href="/css/networker.css?v=2"><link rel="stylesheet" href="/css/networker-guide.css?v=1"><script src="/js/theme.js?v=5"></script></head><body class="nw-body"><main class="nw-app"><header class="nw-top"><div style="display:flex;align-items:center;gap:.65rem"><a href="/dashboard" class="nw-back">← Dashboard</a><a href="/networker" class="nw-btn">NETZPLÄNE</a><button class="mf-theme" type="button" aria-label="Theme umschalten"></button></div><div class="nw-brand"><b>NetWorker</b><span>NETZPLAN · DIAGNOSE</span></div><span class="mf-a11y-host"></span></header><section id="nwView" class="nw-view"><div class="nw-loading">NETZWERKPLÄNE WERDEN KALIBRIERT …</div></section></main><script src="/js/networker.js?v=1"></script><script src="/js/networker-guide.js?v=1"></script> <footer th:replace="~{fragments/layout :: footer}"></footer>
</body></html>
+1 -8
View File
@@ -72,14 +72,7 @@
</div> </div>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</div> </div>
+1 -8
View File
@@ -149,14 +149,7 @@
</script> </script>
<!-- Footer --> <!-- Footer -->
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
@@ -29,5 +29,6 @@
</form> </form>
</section> </section>
</main> </main>
<footer th:replace="~{fragments/layout :: footer}"></footer>
</body> </body>
</html> </html>
@@ -1 +1,2 @@
<!doctype html><html lang="de" data-theme="dark"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>ServiceClash · SysnaptiK Neural</title><link rel="stylesheet" href="/css/theme.css?v=4"><link rel="stylesheet" href="/css/serviceclash.css?v=2"><script src="/js/theme.js?v=5"></script></head><body class="sc-body"><main class="sc-app"><header class="sc-top"><div class="sc-nav"><a href="/dashboard">← Dashboard</a><a href="/serviceclash" class="sc-nav-button">SCHICHTEN</a><button class="mf-theme" type="button" aria-label="Theme umschalten"></button></div><div class="sc-brand"><b>ServiceClash</b><span>LEITSTAND · TRIAGE</span></div><span class="mf-a11y-host"></span></header><section id="scView" class="sc-view"><p class="sc-loading">LEITSTAND WIRD HOCHGEFAHREN …</p></section></main><script src="/js/serviceclash.js?v=2"></script></body></html> <!doctype html><html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>ServiceClash · SysnaptiK Neural</title><link rel="stylesheet" href="/css/theme.css?v=4"><link rel="stylesheet" href="/css/serviceclash.css?v=2"><script src="/js/theme.js?v=5"></script></head><body class="sc-body"><main class="sc-app"><header class="sc-top"><div class="sc-nav"><a href="/dashboard">← Dashboard</a><a href="/serviceclash" class="sc-nav-button">SCHICHTEN</a><button class="mf-theme" type="button" aria-label="Theme umschalten"></button></div><div class="sc-brand"><b>ServiceClash</b><span>LEITSTAND · TRIAGE</span></div><span class="mf-a11y-host"></span></header><section id="scView" class="sc-view"><p class="sc-loading">LEITSTAND WIRD HOCHGEFAHREN …</p></section></main><script src="/js/serviceclash.js?v=2"></script> <footer th:replace="~{fragments/layout :: footer}"></footer>
</body></html>
+1 -8
View File
@@ -127,14 +127,7 @@
}); });
</script> </script>
<footer class="mf-footer"> <footer th:replace="~{fragments/layout :: footer}"></footer>
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body> </body>
</html> </html>
+1 -1
View File
@@ -26,5 +26,5 @@
</section> </section>
</section> </section>
</main> </main>
<footer class="mf-footer"><a href="#">Support</a><a href="#">Feedback</a><a href="/nutzungsbedingungen">Nutzungsbedingungen</a><a href="/richtlinien">Richtlinien</a><a href="/datenschutz">Datenschutz</a><a href="/impressum">Impressum</a></footer> <footer th:replace="~{fragments/layout :: footer}"></footer>
</body></html> </body></html>
@@ -59,5 +59,6 @@
</div> </div>
<script src="/js/syntaxsaga.js?v=1" defer></script> <script src="/js/syntaxsaga.js?v=1" defer></script>
<footer th:replace="~{fragments/layout :: footer}"></footer>
</body> </body>
</html> </html>
@@ -77,5 +77,6 @@
</div> </div>
<script src="/js/techtacular.js?v=4" defer></script> <script src="/js/techtacular.js?v=4" defer></script>
<footer th:replace="~{fragments/layout :: footer}"></footer>
</body> </body>
</html> </html>