Feature+Security: DSGVO-Datenexport, auth-geschützte Media, Datenschutzerklärung v2 (SW by-v880)

This commit is contained in:
rene 2026-05-12 17:28:16 +02:00
parent 465dc2e4d3
commit bf1087c5e1
7 changed files with 264 additions and 27 deletions

View file

@ -101,9 +101,9 @@
</script>
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
<link rel="stylesheet" href="/css/design-system.css?v=879">
<link rel="stylesheet" href="/css/layout.css?v=879">
<link rel="stylesheet" href="/css/components.css?v=879">
<link rel="stylesheet" href="/css/design-system.css?v=880">
<link rel="stylesheet" href="/css/layout.css?v=880">
<link rel="stylesheet" href="/css/components.css?v=880">
</head>
<body>
@ -583,10 +583,10 @@
<div id="modal-container"></div>
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
<script src="/js/api.js?v=879"></script>
<script src="/js/ui.js?v=879"></script>
<script src="/js/app.js?v=879"></script>
<script src="/js/worlds.js?v=879"></script>
<script src="/js/api.js?v=880"></script>
<script src="/js/ui.js?v=880"></script>
<script src="/js/app.js?v=880"></script>
<script src="/js/worlds.js?v=880"></script>
<!-- Feature-Seiten werden lazy geladen -->

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '879'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VER = '880'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.5.1'; // ← semantische Version, wird bei make release gesetzt
const IS_STAGING = location.hostname === 'staging.banyaro.app';
// Cache-Bust-Parameter nach Update-Reload sofort entfernen

View file

@ -102,13 +102,20 @@ window.Page_datenschutz = (() => {
<p style="${S.p};margin-top:var(--space-3)">
Als Ausweichlösung bei Nichtverfügbarkeit des lokalen Modells wird
<strong>Claude Sonnet 4.6</strong> von Anthropic, PBC (San Francisco, USA) genutzt.
In diesem Fall wird ausschließlich der Inhalt deiner Anfrage (Prompt-Text) übermittelt
keine Account- oder Profildaten. Die Übermittlung in die USA erfolgt auf Basis der
EU-Standardvertragsklauseln (Art. 46 Abs. 2 lit. c DSGVO).
In diesem Fall wird der Inhalt deiner Anfrage übermittelt. Bei Gesundheits- und
Ernährungsberichten kann dies Hundedaten (Name, Rasse, Gewicht, Impfhistorie,
Medikamente, Allergien) als Teil des Anfragetextes umfassen. Die Übermittlung
in die USA erfolgt auf Basis der EU-Standardvertragsklauseln (Art. 46 Abs. 2 lit. c DSGVO).
Datenschutzerklärung von Anthropic:
<a href="https://www.anthropic.com/privacy" target="_blank" rel="noopener"
style="${S.a}">anthropic.com/privacy</a>.
</p>
<p style="${S.p};margin-top:var(--space-3)">
Die <strong>Rassenerkennung per Foto</strong> sendet das hochgeladene Bild direkt an
Claude von Anthropic (USA) zur Analyse es gibt hierfür keinen lokalen Fallback.
Das Foto wird nicht dauerhaft bei Anthropic gespeichert. Rechtsgrundlage: Einwilligung
gem. Art. 6 Abs. 1 lit. a DSGVO durch aktive Nutzung der Funktion.
</p>
<p style="${S.p};margin-top:var(--space-3)">
Der <strong>KI-Trainer</strong> analysiert deinen bisherigen Trainingsfortschritt
(Übungshistorie, Erfolgsquoten, Streaks) und gibt personalisierte Empfehlungen.
@ -122,19 +129,31 @@ window.Page_datenschutz = (() => {
findet nicht statt.
</p>`)}
${sec('Wetterdaten (Open-Meteo)', `
${sec('Wetterdaten & Kartendienste', `
<p style="${S.p}">
Die Wetter-Funktion übermittelt auf Wunsch deine GPS-Koordinaten einmalig an
<strong>Open-Meteo</strong> (Österreich, DSGVO-konform), um die lokale
Wettervorhersage abzurufen. Es werden ausschließlich anonyme Koordinaten übertragen
keine Account- oder Profildaten. Open-Meteo protokolliert keine personenbezogenen
Daten. Die Funktion wird nur aktiv, wenn du deinen Standort im Browser freigibst.
Die Wetter-Funktion übermittelt auf Wunsch deine GPS-Koordinaten serverseitig an
<strong>Open-Meteo</strong> (Österreich, DSGVO-konform) für die Wettervorhersage.
Für Wetter-Kartenlayer (Regenradar, Temperaturen) werden Kacheln von
<strong>OpenWeatherMap</strong> (OpenWeather Ltd., UK/USA) geladen dabei wird
dein Browser direkt kontaktiert. Es werden keine Account-Daten übermittelt.
Rechtsgrundlage: Einwilligung gem. Art. 6 Abs. 1 lit. a DSGVO.
</p>
<p style="${S.p};margin-top:var(--space-3)">
Datenschutzerklärung von Open-Meteo:
Für die automatische Ortsnamens-Ermittlung (z. B. im Wetter-Detail) werden deine
GPS-Koordinaten serverseitig an <strong>Nominatim</strong> der OpenStreetMap Foundation
(UK) übermittelt. Es werden ausschließlich Koordinaten weitergegeben keine
personenbezogenen Daten.
</p>
<p style="${S.p};margin-top:var(--space-3)">
Datenschutzerklärung Open-Meteo:
<a href="https://open-meteo.com/en/terms" target="_blank" rel="noopener"
style="${S.a}">open-meteo.com/en/terms</a>
style="${S.a}">open-meteo.com/en/terms</a> ·
OpenWeatherMap:
<a href="https://openweathermap.org/privacy-policy" target="_blank" rel="noopener"
style="${S.a}">openweathermap.org/privacy-policy</a> ·
OpenStreetMap/Nominatim:
<a href="https://osmfoundation.org/wiki/Privacy_Policy" target="_blank" rel="noopener"
style="${S.a}">osmfoundation.org</a>
</p>`)}
${sec('Routenvorschläge (OpenRouteService)', `
@ -200,7 +219,16 @@ window.Page_datenschutz = (() => {
(Art. 16), <strong>Löschung</strong> (Art. 17), <strong>Einschränkung der Verarbeitung</strong>
(Art. 18) sowie <strong>Datenportabilität</strong> (Art. 20). Erteilte Einwilligungen
kannst du jederzeit mit Wirkung für die Zukunft widerrufen (Art. 7 Abs. 3 DSGVO).
Zur Ausübung deiner Rechte wende dich per E-Mail an
</p>
<p style="${S.p};margin-top:var(--space-3)">
<strong>Datenexport (Art. 20 DSGVO):</strong> Du kannst jederzeit unter
Einstellungen Meine Daten exportieren" eine vollständige Kopie deiner
gespeicherten Daten als JSON-Datei herunterladen. Der Export enthält Profildaten,
Hundedaten, Tagebuch, Gesundheitseinträge, Trainingsfortschritt, Ausgaben,
Verhaltensprotokoll, Forum-Beiträge und Gassi-Teilnahmen.
</p>
<p style="${S.p};margin-top:var(--space-3)">
Zur Ausübung weiterer Rechte wende dich per E-Mail an
<a href="mailto:hallo@banyaro.app" style="${S.a}">hallo@banyaro.app</a>.<br><br>
Du hast außerdem das Recht, bei der zuständigen Datenschutz-Aufsichtsbehörde
Beschwerde einzulegen:<br>
@ -212,14 +240,14 @@ window.Page_datenschutz = (() => {
${sec('Speicherdauer', `
<p style="${S.p}">
Deine Daten werden gelöscht, sobald du deinen Account löschst. Server-Logs
werden nach 30 Tagen automatisch gelöscht. Öffentlich gepostete Inhalte
(Forenbeiträge, Giftköder-Meldungen) bleiben nach Account-Löschung anonymisiert
erhalten, sofern sie für die Community relevant sind.
Deine Daten werden vollständig gelöscht, sobald du deinen Account löschst
einschließlich Tagebuch, Gesundheitseinträge, Fotos, Forenbeiträge und Hundeprofil.
Es gibt keine anonymisierte Weiterverarbeitung deiner Inhalte nach Account-Löschung.
Server-Logs werden nach 30 Tagen rotiert.
</p>`)}
<p style="font-size:var(--text-xs);color:var(--c-text-muted);margin:0">
Stand: Mai 2026
Stand: Mai 2026 · Version 2
</p>
</div>

View file

@ -294,6 +294,15 @@ window.Page_settings = (() => {
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#sign-out"></use></svg>
Abmelden
</button>
<button id="settings-export-btn"
style="width:100%;margin-top:var(--space-2);display:flex;align-items:center;justify-content:center;
gap:var(--space-2);padding:var(--space-2) var(--space-4);
border-radius:var(--radius-md);border:none;
background:none;color:var(--c-text-secondary);
font-size:var(--text-xs);cursor:pointer">
<svg class="ph-icon" aria-hidden="true" style="width:12px;height:12px"><use href="/icons/phosphor.svg#download-simple"></use></svg>
Meine Daten exportieren (DSGVO Art. 20)
</button>
<button id="settings-delete-account-btn"
style="width:100%;margin-top:var(--space-2);display:flex;align-items:center;justify-content:center;
gap:var(--space-2);padding:var(--space-2) var(--space-4);
@ -892,6 +901,32 @@ window.Page_settings = (() => {
_render();
});
document.getElementById('settings-export-btn')?.addEventListener('click', async () => {
const btn = document.getElementById('settings-export-btn');
await UI.asyncButton(btn, async () => {
try {
const resp = await fetch('/api/profile/export', {
credentials: 'include',
headers: { 'Authorization': `Bearer ${localStorage.getItem('by_token') || ''}` },
});
if (!resp.ok) throw new Error('Export fehlgeschlagen.');
const data = await resp.json();
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `banyaro-export-${new Date().toISOString().slice(0,10)}.json`;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
UI.toast.success('Export heruntergeladen.');
} catch (err) {
UI.toast.error(err.message || 'Fehler beim Export.');
}
});
});
document.getElementById('settings-delete-account-btn')?.addEventListener('click', async () => {
const ok = await UI.modal.confirm({
title: 'Konto unwiderruflich löschen?',

View file

@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache
============================================================ */
const CACHE_VERSION = 'by-v879';
const CACHE_VERSION = 'by-v880';
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache