diff --git a/backend/src/app.js b/backend/src/app.js index 5dbb1a8..9c18440 100644 --- a/backend/src/app.js +++ b/backend/src/app.js @@ -137,3 +137,14 @@ app.listen(PORT, '0.0.0.0', () => { console.log(`TurfTracker API server running on port ${PORT}`); console.log(`Environment: ${process.env.NODE_ENV || 'development'}`); }); +// Disable etags and set no-store by default to avoid stale cached API responses +app.set('etag', false); + +app.use((req, res, next) => { + // Allow icon proxy to control its own caching + if (req.path && req.path.startsWith('/api/weather/icon')) return next(); + res.setHeader('Cache-Control', 'no-store'); + res.setHeader('Pragma', 'no-cache'); + res.setHeader('Expires', '0'); + next(); +}); diff --git a/frontend/src/App.js b/frontend/src/App.js index e3df9ae..4f64dac 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -48,14 +48,18 @@ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: (failureCount, error) => { - // Don't retry on 401 (unauthorized) or 403 (forbidden) if (error?.response?.status === 401 || error?.response?.status === 403) { return false; } return failureCount < 2; }, - staleTime: 5 * 60 * 1000, // 5 minutes - cacheTime: 10 * 60 * 1000, // 10 minutes + // Always consider data stale so we refetch on mount/focus + staleTime: 0, + // Keep cache tiny and refetch aggressively + cacheTime: 60 * 1000, + refetchOnWindowFocus: true, + refetchOnReconnect: true, + refetchOnMount: 'always', }, }, }); diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index 4ce4f84..5ab9cf2 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -10,6 +10,9 @@ export const apiClient = axios.create({ timeout: 10000, headers: { 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache', + 'Pragma': 'no-cache', + 'Expires': '0', }, }); @@ -33,6 +36,15 @@ apiClient.interceptors.request.use( if (token && token !== 'undefined' && token !== 'null') { config.headers.Authorization = `Bearer ${token}`; } + // Prevent browser/proxy caching of GET requests by adding a timestamp + if ((config.method || 'get').toLowerCase() === 'get') { + config.headers['Cache-Control'] = 'no-cache'; + config.headers['Pragma'] = 'no-cache'; + config.headers['Expires'] = '0'; + const params = new URLSearchParams(config.params || {}); + if (!params.has('_ts')) params.set('_ts', Date.now().toString()); + config.params = Object.fromEntries(params.entries()); + } return config; }, (error) => {