From 8853bce2ec46bcd59f3d3bec8a96ad2a4941a1b1 Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Fri, 31 Jul 2026 13:19:33 +0200 Subject: [PATCH] =?UTF-8?q?Impressum,=20Datenschutzerkl=C3=A4rung=20und=20?= =?UTF-8?q?DSGVO-L=C3=B6schfunktion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /impressum (§ 5 DDG) und /datenschutz als öffentliche Seiten im Brand-Look; Betreiber-Angaben (Name, Anschrift, E-Mail, Zusatz) liegen als Settings und sind bewusst Owner-only — Team-Mitglieder mit settings-Scope kommen an die private Anschrift nicht heran - Die Datenschutzerklärung beschreibt die tatsächliche Verarbeitung dieser Instanz: Server-Logs, Session-Cookies (einwilligungsfrei nach § 25 Abs. 2 TDDDG, kein Tracking), Discord-Login, Level, Wünsche, Geburtstage, Playtester/Alpha-Keys, Bewerbungen, Tickets/Modmail, Erinnerungen, Moderation und Audit-Log - DSGVO Art. 17: DELETE /api/profile löscht XP, Geburtstag, Playtester-Eintrag, Abstimmungen, Erinnerungen, Rollen-Sicherung, Giveaway-Teilnahmen und gibt den Alpha-Key wieder frei; Button im Profil unter „Meine Daten", Löschung landet im Audit-Log - Footer verlinkt beide Rechtstexte, neues GET /api/legal (öffentlich) Co-Authored-By: Claude Fable 5 --- README.md | 3 +- frontend/src/App.jsx | 7 + frontend/src/pages/Datenschutz.jsx | 197 +++++++++++++++++++++++++++++ frontend/src/pages/Impressum.jsx | 97 ++++++++++++++ frontend/src/pages/Profil.jsx | 37 +++++- frontend/src/pages/Settings.jsx | 25 ++++ frontend/src/style.css | 38 ++++++ src/db.js | 23 ++++ src/runtime-settings.js | 10 ++ src/web/api.js | 31 ++++- 10 files changed, 464 insertions(+), 4 deletions(-) create mode 100644 frontend/src/pages/Datenschutz.jsx create mode 100644 frontend/src/pages/Impressum.jsx diff --git a/README.md b/README.md index 7529c2e..0f752ab 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,8 @@ SQLite (better-sqlite3, FTS5), Docker Multi-Stage — deploybar als Portainer-St | `/events` | öffentlich | Discord-Events (Playtests, Streams …) | | `/changelog` | öffentlich | Alle Releases mit Notes, Tag- und Pre-Release-Chips | | `/feed.xml` | öffentlich | RSS-Feed der Devlogs | -| `/profil` | Member | Eigenes Profil: Rang, XP, Rollen (togglebar), Alpha-Key | +| `/profil` | Member | Eigenes Profil: Rang, XP, Rollen (togglebar), Alpha-Key, DSGVO-Löschung | +| `/impressum` · `/datenschutz` | öffentlich | Rechtstexte — Betreiber-Angaben aus dem Setup (Owner-only) | | `/` | öffentlich | Startseite: Hero, Live-Zahlen (Member/Server/Spieler), Bereichs-Kacheln, neuestes Devlog | | `/server` | öffentlich | Live-Status aller Game-Server: Logo, Spieler-Balken, 24h-Verlauf, Uptime & Peak, Copy-Adresse | | `/commits` | nur Admin | Archivierte Commits aller Repos (SHA → Gitea-Link) | diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 1825d30..b893aac 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -13,6 +13,8 @@ import Profil from './pages/Profil.jsx'; import Server from './pages/Server.jsx'; import Home from './pages/Home.jsx'; import DevlogDetail from './pages/DevlogDetail.jsx'; +import Impressum from './pages/Impressum.jsx'; +import Datenschutz from './pages/Datenschutz.jsx'; const COMMUNITY_LINKS = [ ['/galerie', 'Galerie'], @@ -196,6 +198,8 @@ export default function App() { } /> } /> } /> + } /> + } /> } /> } /> } /> @@ -208,6 +212,9 @@ export default function App() { git.d4rkst3r.de + // + Impressum + Datenschutz ); diff --git a/frontend/src/pages/Datenschutz.jsx b/frontend/src/pages/Datenschutz.jsx new file mode 100644 index 0000000..7a3bdea --- /dev/null +++ b/frontend/src/pages/Datenschutz.jsx @@ -0,0 +1,197 @@ +// Datenschutzerklärung — beschreibt die tatsächliche Verarbeitung dieser Instanz +// (Discord-Login, Community-Funktionen, Moderation). Betreiber-Angaben aus den Settings. +import { useEffect, useState } from 'react'; +import { apiGet } from '../api.js'; + +export default function Datenschutz() { + const [data, setData] = useState(null); + + useEffect(() => { + apiGet('/api/legal').then(setData).catch(() => setData({ legal: {} })); + window.scrollTo(0, 0); + }, []); + + const legal = data?.legal ?? {}; + const brand = data?.brand ?? 'Wir'; + + return ( + <> +
+
LEGAL
+
+ +
+

// 1. Verantwortlicher

+

+ Verantwortlich für die Datenverarbeitung auf dieser Webseite im Sinne der + Datenschutz-Grundverordnung (DSGVO) ist:

+ {legal.name || '—'}
+ {(legal.address || '').split('\n').map((line, i) => ( + {line}
+ ))} + {legal.email && <>E-Mail: {legal.email}} +

+ +

// 2. Aufruf der Webseite (Server-Logs)

+

+ Beim Aufruf dieser Seite verarbeitet der Server technische Zugriffsdaten: + IP-Adresse, Zeitpunkt der Anfrage, aufgerufene Adresse, HTTP-Statuscode und + die Browserkennung. Diese Daten sind technisch erforderlich, um die Seite + auszuliefern und Störungen sowie Angriffe erkennen zu können. Rechtsgrundlage + ist Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse am sicheren und + stabilen Betrieb). Die Logdaten werden nicht mit anderen Datenquellen + zusammengeführt und nach kurzer Zeit im Rahmen der üblichen Rotation gelöscht. +

+ +

// 3. Cookies

+

+ Diese Seite verwendet ausschließlich technisch notwendige Cookies. Konkret + wird nach einem Login ein signiertes Sitzungs-Cookie gesetzt, damit du + eingeloggt bleibst; ein weiteres kurzlebiges Cookie sichert den Login-Vorgang + gegen Missbrauch ab (CSRF-Schutz). Diese Cookies sind für den von dir + gewünschten Dienst unbedingt erforderlich und daher nach § 25 Abs. 2 Nr. 2 + TDDDG einwilligungsfrei. Es findet kein Tracking, keine + Reichweitenmessung und keine Werbung statt. Die verwendeten Schriftarten + werden lokal von diesem Server ausgeliefert — es entsteht dabei keine + Verbindung zu Dritten. +

+ +

// 4. Login mit Discord

+

+ Für den Mitglieder-Bereich kannst du dich freiwillig mit deinem + Discord-Konto anmelden (OAuth2, Berechtigung identify). Dabei + erhalten wir von Discord deine Benutzer-ID, deinen Benutzernamen und dein + Profilbild. Diese Angaben werden in dem oben genannten Sitzungs-Cookie + gespeichert; ein Zugriffstoken speichern wir nicht. Zusätzlich prüfen wir + beim Login, ob du Mitglied unseres Discord-Servers bist, da der Zugang + Mitgliedern vorbehalten ist. Rechtsgrundlage ist Art. 6 Abs. 1 lit. a DSGVO + (Einwilligung durch die Anmeldung) bzw. lit. b DSGVO für die Bereitstellung + des Mitglieder-Bereichs. Du kannst dich jederzeit über den Logout abmelden; + das Cookie wird dann gelöscht. +

+

+ Anbieter ist Discord Netherlands BV bzw. Discord Inc., 444 De Haro Street, + San Francisco, CA 94107, USA. Beim Login und beim Anzeigen von Profilbildern + (cdn.discordapp.com) werden Daten an Discord übertragen und + gegebenenfalls in den USA verarbeitet. Details findest du in der + Datenschutzerklärung von Discord. +

+ +

// 5. Community-Funktionen

+

+ {brand} betreibt einen eigenen Discord-Bot, der zur Verwaltung der Community + Daten speichert. Verarbeitet werden — je nachdem, welche Funktionen du nutzt: +

+
    +
  • + Level-System: Benutzer-ID, Anzeigename und gesammelte + Erfahrungspunkte, um eine Bestenliste anzuzeigen. +
  • +
  • + Feature-Wünsche & Abstimmungen: dein Anzeigename und der + Text deines Vorschlags, bei Abstimmungen deine Benutzer-ID (damit jede + Person nur einmal abstimmt). +
  • +
  • + Geburtstage: Tag und Monat deines Geburtstags — nur wenn du + sie selbst per Befehl einträgst, jederzeit selbst wieder entfernbar. +
  • +
  • + Playtester & Zugangsschlüssel: Benutzer-ID, Anzeigename und + Zeitpunkt der Anmeldung sowie ein dir zugeteilter Alpha-Key. +
  • +
  • + Bewerbungen: deine Antworten auf die Formularfragen sowie das + Ergebnis der Prüfung. +
  • +
  • + Support & Kontakt: Inhalte von Tickets und Direktnachrichten + an den Bot, damit das Team antworten kann; beim Schließen eines Tickets + wird auf Wunsch ein Gesprächsverlauf erstellt. +
  • +
  • + Erinnerungen: Text und Zeitpunkt, bis die Erinnerung + zugestellt wurde. +
  • +
  • + Veröffentlichte Beiträge: Entwicklungsberichte und + Community-Screenshots werden mit Anzeigenamen archiviert und auf dieser + Webseite dargestellt. +
  • +
+

+ Rechtsgrundlage ist Art. 6 Abs. 1 lit. a DSGVO (Einwilligung) für alle + freiwilligen Angaben und Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse + am Betrieb einer funktionierenden Community) für den technischen Betrieb. +

+ +

// 6. Moderation und Protokolle

+

+ Zur Durchsetzung der Serverregeln werden Verwarnungen mit Benutzer-ID, Grund + und Zeitpunkt gespeichert. Änderungen im Administrationsbereich der Webseite + werden in einem Protokoll festgehalten (wer wann welche Einstellung geändert + hat), um Missbrauch nachvollziehen zu können. Rechtsgrundlage ist Art. 6 + Abs. 1 lit. f DSGVO. +

+ +

// 7. Speicherdauer

+

+ Wir speichern personenbezogene Daten so lange, wie du Mitglied der Community + bist oder die jeweilige Funktion nutzt. Verlässt du den Discord-Server, kannst + du die Löschung deiner Daten verlangen (siehe Punkt 9). Sitzungs-Cookies + laufen spätestens nach sieben Tagen ab. Von der Datenbank werden verschlüsselte + Sicherungskopien erstellt, die nach spätestens 14 Tagen automatisch gelöscht + werden. +

+ +

// 8. Weitergabe an Dritte

+

+ Eine Weitergabe deiner Daten zu Werbezwecken findet nicht statt. Ein Verkauf + von Daten erfolgt nicht. Übermittlungen erfolgen nur an Discord, soweit dies + für die Nutzung des Discord-Servers und des Logins technisch notwendig ist. +

+ +

// 9. Deine Rechte

+

+ Du hast jederzeit das Recht auf Auskunft über die zu deiner Person + gespeicherten Daten (Art. 15 DSGVO), auf Berichtigung (Art. 16), auf Löschung + (Art. 17), auf Einschränkung der Verarbeitung (Art. 18), auf + Datenübertragbarkeit (Art. 20) sowie auf Widerspruch gegen die Verarbeitung + (Art. 21). Eine erteilte Einwilligung kannst du jederzeit mit Wirkung für die + Zukunft widerrufen. +

+

+ Selbst löschen: Eingeloggte Mitglieder können ihre gespeicherten + Daten direkt auf der Profilseite löschen — dort findest du unten den Bereich + „Meine Daten löschen". Beiträge, die im Discord stehen (etwa Screenshots oder + Nachrichten), löschst du bitte dort; melde dich sonst gerne bei uns. +

+

+ Für Anfragen genügt eine formlose Nachricht an + {' '}{legal.email ? {legal.email} : 'die im Impressum genannte Adresse'}. +

+ +

// 10. Beschwerderecht

+

+ Wenn du der Ansicht bist, dass die Verarbeitung deiner Daten gegen die DSGVO + verstößt, kannst du dich bei einer Datenschutz-Aufsichtsbehörde beschweren — + etwa bei der für deinen Wohnsitz zuständigen Landesdatenschutzbehörde. +

+ +

// 11. Änderungen

+

+ Wir passen diese Datenschutzerklärung an, wenn sich die angebotenen Funktionen + ändern. Es gilt jeweils die hier veröffentlichte Fassung. +

+
+ + ); +} diff --git a/frontend/src/pages/Impressum.jsx b/frontend/src/pages/Impressum.jsx new file mode 100644 index 0000000..a8a4955 --- /dev/null +++ b/frontend/src/pages/Impressum.jsx @@ -0,0 +1,97 @@ +// Impressum nach § 5 DDG — die Betreiber-Angaben kommen aus den Settings +// (System-Tab, nur Owner), damit hier nichts hartkodiert im Repo landet. +import { useEffect, useState } from 'react'; +import { apiGet } from '../api.js'; + +export default function Impressum() { + const [data, setData] = useState(null); + + useEffect(() => { + apiGet('/api/legal').then(setData).catch(() => setData({ legal: {} })); + window.scrollTo(0, 0); + }, []); + + const legal = data?.legal ?? {}; + const incomplete = !legal.name || !legal.address || !legal.email; + + return ( + <> +
+
LEGAL
+
+ +
+ {incomplete && ( +

+ ⚠️ Die Impressums-Angaben sind noch nicht vollständig hinterlegt + (Setup → System → Impressum). +

+ )} + +

// Diensteanbieter

+

+ {legal.name || '—'}
+ {(legal.address || '').split('\n').map((line, i) => ( + {line}
+ ))} +

+ +

// Kontakt

+

+ E-Mail: {legal.email ? {legal.email} : '—'} +

+ + {legal.extra && ( + <> +

// Weitere Angaben

+

{legal.extra.split('\n').map((line, i) => {line}
)}

+ + )} + +

// Verantwortlich für den Inhalt

+

+ Verantwortlich für journalistisch-redaktionelle Inhalte nach § 18 Abs. 2 MStV: + {' '}{legal.name || '—'}{legal.address ? `, ${legal.address.replace(/\n/g, ', ')}` : ''} +

+ +

// Haftung für Inhalte

+

+ Als Diensteanbieter sind wir für eigene Inhalte auf diesen Seiten nach den + allgemeinen Gesetzen verantwortlich. Wir sind jedoch nicht verpflichtet, + übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach + Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. + Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen + nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche + Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten + Rechtsverletzung möglich. Bei Bekanntwerden entsprechender Rechtsverletzungen + entfernen wir diese Inhalte umgehend. +

+ +

// Haftung für Links

+

+ Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte + wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch + keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der + jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Bei Bekanntwerden + von Rechtsverletzungen entfernen wir derartige Links umgehend. +

+ +

// Urheberrecht

+

+ Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten + unterliegen dem deutschen Urheberrecht. Beiträge Dritter — insbesondere + Screenshots aus der Community-Galerie — sind als solche gekennzeichnet. + Sollten Sie auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um + einen entsprechenden Hinweis; bei Bekanntwerden entfernen wir die Inhalte + umgehend. +

+
+ + ); +} diff --git a/frontend/src/pages/Profil.jsx b/frontend/src/pages/Profil.jsx index 42cf456..20d3d02 100644 --- a/frontend/src/pages/Profil.jsx +++ b/frontend/src/pages/Profil.jsx @@ -1,7 +1,8 @@ // Member-Profil: Rang & XP, Discord-Rollen, Alpha-Key, Playtester-Status // + Rollen-Selfservice (Rollen-Menüs direkt im Browser togglen). import { useEffect, useState } from 'react'; -import { apiGet, apiPost } from '../api.js'; +import { Link } from 'react-router-dom'; +import { apiGet, apiPost, apiDelete } from '../api.js'; function fmtDate(iso) { if (!iso) return '—'; @@ -15,6 +16,7 @@ export default function Profil({ me }) { const [error, setError] = useState(null); const [busy, setBusy] = useState(''); const [copied, setCopied] = useState(false); + const [wipeMsg, setWipeMsg] = useState(''); useEffect(() => { apiGet('/api/profile').then(setProfile).catch((e) => setError(e?.message ?? 'Fehler')); @@ -40,6 +42,23 @@ export default function Profil({ me }) { setBusy(''); } + async function wipeData() { + if (!window.confirm( + 'Wirklich alle gespeicherten Daten löschen?\n\n' + + 'Betrifft: XP/Level, Geburtstag, Playtester-Eintrag, Abstimmungen, ' + + 'Erinnerungen, Rollen-Sicherung und deinen Alpha-Key.\n\n' + + 'Das lässt sich nicht rückgängig machen.' + )) return; + try { + const res = await apiDelete('/api/profile'); + const total = Object.values(res.deleted).reduce((a, b) => a + b, 0); + setWipeMsg(`✅ ${total} Einträge gelöscht.`); + apiGet('/api/profile').then(setProfile).catch(() => {}); + } catch { + setWipeMsg('⚠️ Löschen fehlgeschlagen — melde dich bitte beim Team.'); + } + } + async function copyKey() { try { await navigator.clipboard.writeText(profile.alphaKey); @@ -156,6 +175,22 @@ export default function Profil({ me }) { ))} )} + + {/* DSGVO Art. 17: eigene Daten löschen */} +
+

// Meine Daten

+

+ Du kannst jederzeit alle Daten löschen, die hier zu dir gespeichert + sind: XP/Level, Geburtstag, Playtester-Eintrag, Abstimmungen, + Erinnerungen, Rollen-Sicherung und einen zugeteilten Alpha-Key. + Was du im Discord gepostet hast, löschst du bitte dort. Details im{' '} + Datenschutz. +

+
+ +
+ {wipeMsg &&

{wipeMsg}

} +
)} diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx index 18ef8be..ac4941c 100644 --- a/frontend/src/pages/Settings.jsx +++ b/frontend/src/pages/Settings.jsx @@ -1219,6 +1219,31 @@ export default function Settings({ me }) { +
+

// Impressum & Datenschutz

+

+ Erscheint auf /impressum und /datenschutz. Für § 5 DDG + ist eine ladungsfähige Anschrift nötig (kein Postfach) — nur der Owner sieht + und ändert diese Felder. +

+
+ + setForm({ ...form, legal_name: e.target.value })} /> +
+
+ +