From 279f4bcfb8833bb9a1453d07d604a91e8db403b8 Mon Sep 17 00:00:00 2001 From: HikeMap User Date: Mon, 5 Jan 2026 13:01:33 -0600 Subject: [PATCH] Fix caching issues - always fetch fresh content from server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Service Worker changes: - Bump version to v1.0.1 to invalidate old caches - Use network-first strategy for all API calls - Use network-first strategy for HTML files - Only use cache-first for static assets (CSS, JS libs, images) Server changes: - Serve service-worker.js with no-cache headers - Serve HTML files with no-cache headers Client changes: - Detect service worker updates and auto-reload page - Check for updates on every page load 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- index.html | 23 +++++++++++++++++++++++ server.js | 14 ++++++++++++++ service-worker.js | 48 ++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index 567df9b..436d1c6 100644 --- a/index.html +++ b/index.html @@ -9850,6 +9850,23 @@ .then(registration => { console.log('Service Worker registered:', registration.scope); + // Check for updates on every page load + registration.update(); + + // Handle service worker updates + registration.addEventListener('updatefound', () => { + const newWorker = registration.installing; + console.log('New service worker installing...'); + + newWorker.addEventListener('statechange', () => { + if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { + // New version available - skip waiting and reload + console.log('New version available, activating...'); + newWorker.postMessage({ type: 'SKIP_WAITING' }); + } + }); + }); + // Check for updates periodically setInterval(() => { registration.update(); @@ -9860,6 +9877,12 @@ }); }); + // Reload page when new service worker takes control + navigator.serviceWorker.addEventListener('controllerchange', () => { + console.log('New service worker activated, reloading...'); + window.location.reload(); + }); + // Listen for app install prompt let deferredPrompt; window.addEventListener('beforeinstallprompt', (e) => { diff --git a/server.js b/server.js index abd28ec..1dbe250 100644 --- a/server.js +++ b/server.js @@ -64,6 +64,20 @@ app.use('/api', (req, res, next) => { next(); }); +// Serve service-worker.js with no-cache (critical for updates) +app.get('/service-worker.js', (req, res) => { + res.set('Cache-Control', 'no-store, no-cache, must-revalidate'); + res.set('Pragma', 'no-cache'); + res.sendFile(path.join(__dirname, 'service-worker.js')); +}); + +// Serve HTML files with no-cache to ensure fresh content +app.get(['/', '/index.html', '/admin.html'], (req, res, next) => { + res.set('Cache-Control', 'no-store, no-cache, must-revalidate'); + res.set('Pragma', 'no-cache'); + next(); +}); + // Serve static files - prioritize data directory for default.kml app.get('/default.kml', async (req, res) => { try { diff --git a/service-worker.js b/service-worker.js index e3679b4..0f94a22 100644 --- a/service-worker.js +++ b/service-worker.js @@ -1,5 +1,6 @@ // HikeMap Service Worker -const CACHE_NAME = 'hikemap-v1.0.0'; +// Increment version to force cache refresh +const CACHE_NAME = 'hikemap-v1.0.1'; const urlsToCache = [ '/', '/index.html', @@ -78,13 +79,51 @@ self.addEventListener('fetch', event => { return; } - // Handle API calls with network-first strategy + // Handle ALL API calls with network-first strategy + if (url.pathname.startsWith('/api/')) { + event.respondWith( + fetch(event.request) + .then(response => { + return response; + }) + .catch(() => { + // Fall back to cache for API calls if offline + return caches.match(event.request); + }) + ); + return; + } + + // Handle HTML files with network-first strategy (always get fresh version) + if (event.request.destination === 'document' || + url.pathname === '/' || + url.pathname.endsWith('.html')) { + event.respondWith( + fetch(event.request) + .then(response => { + // Cache the fresh version + if (response.status === 200) { + const responseToCache = response.clone(); + caches.open(CACHE_NAME).then(cache => { + cache.put(event.request, responseToCache); + }); + } + return response; + }) + .catch(() => { + // Fall back to cache only if network fails + return caches.match(event.request); + }) + ); + return; + } + + // Handle geocache/KML data with network-first if (url.pathname.includes('/save-kml') || url.pathname.includes('/geocaches')) { event.respondWith( fetch(event.request) .then(response => { - // Cache successful API responses if (response.status === 200) { const responseToCache = response.clone(); caches.open(GEOCACHE_CACHE).then(cache => { @@ -94,14 +133,13 @@ self.addEventListener('fetch', event => { return response; }) .catch(() => { - // Fall back to cache for API calls return caches.match(event.request); }) ); return; } - // Default strategy: cache-first for assets + // Default strategy: cache-first for static assets (CSS, JS libraries, images) event.respondWith( caches.match(event.request) .then(response => {