Sprint 10: OSM-POI-Cache, Karten-Clustering, Routen-Redesign
Karte (map.js):
- OSM Overpass API: Restaurants, Tierärzte, Parkplätze, Bänke, Wasserstellen
- Leaflet.markercluster für alle OSM-Layer
- Standort-Dot mit GPS-Genauigkeitskreis, Wake-Lock bei Aufzeichnung
- Community-Pins setzen/löschen, Meldungen, Crosshair-Placement
- Layer-Sichtbarkeit in localStorage (by_map_visible_v1)
Routen (routes.js + routen.py):
- Komoot-Stil: SVG-Track-Preview, Foto-Upload, Nearby-POIs im Detail-Modal
- Neue Felder: is_public, hunde_tauglichkeit, foto_urls
- Rate-Endpoint (POST /api/routes/{id}/rate)
- Foto-Upload (POST /api/routes/{id}/photo)
- Fix: json_extract $[-1] → $[#-1] (SQLite-kompatibler Pfad für letztes Element)
Backend (osm.py, database.py, scheduler.py):
- /api/osm/pois: OSM-Overpass-Cache mit Tile-Logik (14 Tage TTL)
- /api/osm/user-poi: Community-Marker CRUD
- /api/osm/report: Marker als ungültig melden
- Neue Tabellen: osm_pois, osm_tiles, user_map_pois, osm_reports
- Giftköder-Archiv-Job (täglich 03:00, soft-delete nach Ablauf)
- Giftköder-Archiv-Job als APScheduler-CronJob
UI: Orte-Menüpunkt entfernt (in Karte integriert), APP_VER auf 62
This commit is contained in:
parent
bf26e5faf4
commit
ebe4ce20cf
16 changed files with 3020 additions and 737 deletions
|
|
@ -1,10 +1,11 @@
|
|||
/* ============================================================
|
||||
BAN YARO — Service Worker
|
||||
Offline-Cache + Push Notifications
|
||||
Offline-Cache + Push Notifications + Tile-Cache
|
||||
============================================================ */
|
||||
|
||||
const CACHE_VERSION = 'by-v33';
|
||||
const CACHE_VERSION = 'by-v62';
|
||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||
|
||||
// index.html wird NICHT pre-gecacht (immer Network-First)
|
||||
const STATIC_ASSETS = [
|
||||
|
|
@ -14,6 +15,9 @@ const STATIC_ASSETS = [
|
|||
'/js/api.js',
|
||||
'/js/ui.js',
|
||||
'/js/app.js',
|
||||
'/js/leaflet.markercluster.js',
|
||||
'/css/MarkerCluster.css',
|
||||
'/css/MarkerCluster.Default.css',
|
||||
'/manifest.json',
|
||||
'/icons/icon-192.png',
|
||||
];
|
||||
|
|
@ -30,13 +34,15 @@ self.addEventListener('install', event => {
|
|||
});
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// ACTIVATE — alte Caches aufräumen
|
||||
// ACTIVATE — alte Caches aufräumen (CACHE_TILES behalten)
|
||||
// ----------------------------------------------------------
|
||||
self.addEventListener('activate', event => {
|
||||
event.waitUntil(
|
||||
caches.keys()
|
||||
.then(keys => Promise.all(
|
||||
keys.filter(k => k !== CACHE_STATIC).map(k => caches.delete(k))
|
||||
keys
|
||||
.filter(k => k !== CACHE_STATIC && k !== CACHE_TILES)
|
||||
.map(k => caches.delete(k))
|
||||
))
|
||||
.then(() => self.clients.claim())
|
||||
);
|
||||
|
|
@ -59,6 +65,38 @@ self.addEventListener('fetch', event => {
|
|||
return;
|
||||
}
|
||||
|
||||
// OSM-Kartenkacheln: eigener persistenter Cache
|
||||
if (url.hostname.endsWith('tile.openstreetmap.org')) {
|
||||
event.respondWith(
|
||||
caches.open(CACHE_TILES).then(cache =>
|
||||
cache.match(event.request).then(cached => {
|
||||
if (cached) return cached;
|
||||
return fetch(event.request).then(response => {
|
||||
if (response.ok) cache.put(event.request, response.clone());
|
||||
return response;
|
||||
});
|
||||
})
|
||||
).catch(() => new Response('', { status: 503 }))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Seiten-Module (/js/pages/…): immer Network-First (versioniert über ?v=, kein alter Cache-Treffer)
|
||||
if (url.pathname.startsWith('/js/pages/')) {
|
||||
event.respondWith(
|
||||
fetch(event.request)
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
const clone = response.clone();
|
||||
caches.open(CACHE_STATIC).then(c => c.put(event.request, clone));
|
||||
}
|
||||
return response;
|
||||
})
|
||||
.catch(() => caches.match(event.request))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Navigation (index.html): immer Network-First
|
||||
if (event.request.mode === 'navigate') {
|
||||
event.respondWith(
|
||||
|
|
@ -93,6 +131,48 @@ self.addEventListener('fetch', event => {
|
|||
);
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// MESSAGE — Tile-Vorausladung (Offline-Speicherung)
|
||||
// ----------------------------------------------------------
|
||||
self.addEventListener('message', event => {
|
||||
if (event.data?.type !== 'CACHE_TILES') return;
|
||||
|
||||
const urls = event.data.urls || [];
|
||||
const source = event.source;
|
||||
let done = 0;
|
||||
const total = urls.length;
|
||||
|
||||
if (total === 0) {
|
||||
source?.postMessage({ type: 'CACHE_TILES_PROGRESS', done: 0, total: 0 });
|
||||
return;
|
||||
}
|
||||
|
||||
caches.open(CACHE_TILES).then(cache => {
|
||||
const queue = [...urls];
|
||||
|
||||
function fetchBatch() {
|
||||
if (queue.length === 0) {
|
||||
source?.postMessage({ type: 'CACHE_TILES_PROGRESS', done: total, total });
|
||||
return;
|
||||
}
|
||||
const batch = queue.splice(0, 8);
|
||||
Promise.all(batch.map(url =>
|
||||
cache.match(url).then(cached => {
|
||||
if (cached) { done++; return; }
|
||||
return fetch(url, { mode: 'cors' })
|
||||
.then(r => { if (r.ok) cache.put(url, r); done++; })
|
||||
.catch(() => { done++; });
|
||||
})
|
||||
)).then(() => {
|
||||
source?.postMessage({ type: 'CACHE_TILES_PROGRESS', done, total });
|
||||
setTimeout(fetchBatch, 30);
|
||||
});
|
||||
}
|
||||
|
||||
fetchBatch();
|
||||
});
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// PUSH NOTIFICATIONS
|
||||
// ----------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue