Feature: ORS-Wochenlimit (20/Woche), Tages-Cache, Privilegien-Bypass, Datenschutz-Update — SW by-v480, APP_VER 457

This commit is contained in:
rene 2026-04-29 08:23:55 +02:00
parent ca8bb495b0
commit 7048499624
8 changed files with 121 additions and 14 deletions

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '456'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VER = '457'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const App = (() => {

View file

@ -105,6 +105,29 @@ window.Page_datenschutz = (() => {
findet nicht statt.
</p>`)}
${sec('Routenvorschläge (OpenRouteService)', `
<p style="${S.p}">
Die Funktion <strong>Routenvorschläge"</strong> berechnet auf Wunsch einen Rundweg
ausgehend von deinem aktuellen Standort. Dazu werden deine GPS-Koordinaten einmalig
an den Dienst <strong>OpenRouteService</strong> übermittelt, der von
<strong>HeiGIT</strong> am Karlsruher Institut für Technologie (KIT), Deutschland,
betrieben wird. Es werden ausschließlich die Koordinaten übertragen
keine Account- oder Profildaten. OpenRouteService speichert keine
personenbezogenen Daten dauerhaft.
</p>
<p style="${S.p};margin-top:var(--space-3)">
Die Funktion wird nur aktiv, wenn du deinen Standort im Browser freigibst und
bewusst einen Routenvorschlag anforderst (Einwilligung gem. Art. 6 Abs. 1 lit. a DSGVO).
Der Tagesvorschlag auf der Startseite wird nur berechnet, wenn du eingeloggt bist und
Standortzugriff erteilt hast das Ergebnis wird lokal zwischengespeichert und
maximal einmal täglich neu abgerufen.
</p>
<p style="${S.p};margin-top:var(--space-3)">
Datenschutzerklärung von OpenRouteService:
<a href="https://openrouteservice.org/privacy-policy/" target="_blank" rel="noopener"
style="${S.a}">openrouteservice.org/privacy-policy</a>
</p>`)}
${sec('Push-Benachrichtigungen', `
<p style="${S.p}">
Wenn du Push-Benachrichtigungen aktivierst, wird ein Abonnement-Token an den
@ -164,7 +187,7 @@ window.Page_datenschutz = (() => {
</p>`)}
<p style="font-size:var(--text-xs);color:var(--c-text-muted);margin:0">
Stand: April 2026
Stand: Mai 2026
</p>
</div>

View file

@ -490,11 +490,21 @@ window.Page_routes = (() => {
});
_suggestResult = result;
} catch (err) {
const is429 = err.status === 429 || String(err.message).includes('Wochenlimit');
if (res) res.innerHTML = `
<div style="padding:var(--space-4);border-radius:var(--radius-lg);
background:rgba(220,38,38,0.08);border:1px solid rgba(220,38,38,0.25);
color:#f87171;font-size:0.9rem">
${UI.icon('warning')} ${UI.escape(err.message || 'Fehler beim Berechnen des Rundwegs.')}
<div style="padding:var(--space-5);border-radius:var(--radius-lg);
background:${is429 ? 'rgba(234,179,8,0.08)' : 'rgba(220,38,38,0.08)'};
border:1px solid ${is429 ? 'rgba(234,179,8,0.3)' : 'rgba(220,38,38,0.25)'};
color:${is429 ? '#facc15' : '#f87171'};text-align:center">
<svg class="ph-icon" style="width:28px;height:28px;margin-bottom:var(--space-3)" aria-hidden="true">
<use href="/icons/phosphor.svg#${is429 ? 'calendar-x' : 'warning'}"></use>
</svg>
<p style="margin:0;font-size:var(--text-sm);font-weight:var(--weight-semibold)">
${is429 ? 'Wochenlimit erreicht' : 'Fehler beim Berechnen'}
</p>
<p style="margin:var(--space-2) 0 0;font-size:var(--text-xs);opacity:0.85">
${is429 ? 'Du hast diese Woche alle 20 Routenvorschläge genutzt. Montag gibt es neue.' : UI.escape(err.message || 'Unbekannter Fehler')}
</p>
</div>`;
if (calcBtn) calcBtn.disabled = false;
return;
@ -515,8 +525,14 @@ window.Page_routes = (() => {
: '';
const diffLabel = { leicht: 'Leicht', mittel: 'Mittel', anspruchsvoll: 'Schwer' }[result.schwierigkeit] || '';
const limitHint = (result.weekly_remaining != null)
? `<p style="font-size:var(--text-xs);color:var(--c-text-muted);text-align:right;margin:0 0 var(--space-2)">
Noch ${result.weekly_remaining} von 20 Anfragen diese Woche
</p>`
: '';
res.innerHTML = `
<div id="rks-map" style="height:250px;background:var(--c-surface);margin-bottom:var(--space-3)"></div>
${limitHint}<div id="rks-map" style="height:250px;background:var(--c-surface);margin-bottom:var(--space-3)"></div>
<div style="display:flex;gap:var(--space-3);align-items:center;flex-wrap:wrap;margin-bottom:var(--space-4)">
<span style="${_pillStyle('rgba(107,114,128,0.10)','#9ca3af','rgba(107,114,128,0.30)')}">
${UI.icon('map-trifold')} ${UI.escape(distStr)}

View file

@ -375,12 +375,34 @@ window.Page_welcome = (() => {
const km = [2, 4, 6][dayIdx % 3];
const seed = dayIdx % 5;
// Tages-Cache prüfen — ORS nur einmal pro Tag anfragen
const today = new Date().toISOString().slice(0, 10); // 'YYYY-MM-DD'
const cacheKey = 'by_daily_route_' + today;
const cached = localStorage.getItem(cacheKey);
if (cached) {
try {
const result = JSON.parse(cached);
_applyRouteChip(result, km, seed);
return;
} catch {}
}
let result;
try {
result = await API.post('/routes/suggest', { lat: loc.lat, lon: loc.lon, distance_km: km, seed });
} catch { return; }
if (!result?.gps_track?.length) return;
// Ergebnis cachen und alte Einträge aufräumen
localStorage.setItem(cacheKey, JSON.stringify(result));
Object.keys(localStorage)
.filter(k => k.startsWith('by_daily_route_') && k !== cacheKey)
.forEach(k => localStorage.removeItem(k));
_applyRouteChip(result, km, seed);
}
function _applyRouteChip(result, km, seed) {
const chipsRow = _container.querySelector('#wc-chips-row');
if (!chipsRow) return;

View file

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