fix: auto-invalidate cache on version update — closes #121

- Add version check on app startup: compare server version with stored
  client version, clear all SW caches and reload on mismatch
- Set Cache-Control: no-cache on index.html so browsers always fetch
  the latest version instead of serving stale cached HTML
This commit is contained in:
Maurice
2026-03-30 10:26:23 +02:00
parent 3059d53d11
commit 8d9a511edf
2 changed files with 33 additions and 2 deletions

View File

@@ -69,9 +69,32 @@ export default function App() {
if (token) {
loadUser()
}
authApi.getAppConfig().then((config: { demo_mode?: boolean; has_maps_key?: boolean }) => {
authApi.getAppConfig().then(async (config: { demo_mode?: boolean; has_maps_key?: boolean; version?: string }) => {
if (config?.demo_mode) setDemoMode(true)
if (config?.has_maps_key !== undefined) setHasMapsKey(config.has_maps_key)
// Version-based cache invalidation: clear all caches on version change
if (config?.version) {
const storedVersion = localStorage.getItem('trek_app_version')
if (storedVersion && storedVersion !== config.version) {
try {
// Clear all Service Worker caches
if ('caches' in window) {
const names = await caches.keys()
await Promise.all(names.map(n => caches.delete(n)))
}
// Unregister all service workers
if ('serviceWorker' in navigator) {
const regs = await navigator.serviceWorker.getRegistrations()
await Promise.all(regs.map(r => r.unregister()))
}
} catch {}
localStorage.setItem('trek_app_version', config.version)
window.location.reload()
return
}
localStorage.setItem('trek_app_version', config.version)
}
}).catch(() => {})
}, [])

View File

@@ -163,8 +163,16 @@ app.use('/api/backup', backupRoutes);
// Serve static files in production
if (process.env.NODE_ENV === 'production') {
const publicPath = path.join(__dirname, '../public');
app.use(express.static(publicPath));
app.use(express.static(publicPath, {
setHeaders: (res, filePath) => {
// Never cache index.html so version updates are picked up immediately
if (filePath.endsWith('index.html')) {
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
}
},
}));
app.get('*', (req: Request, res: Response) => {
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.sendFile(path.join(publicPath, 'index.html'));
});
}