diff --git a/client/index.html b/client/index.html index 0e4e508..582bc34 100644 --- a/client/index.html +++ b/client/index.html @@ -21,7 +21,9 @@ - +
diff --git a/client/package-lock.json b/client/package-lock.json index 5dc9bc0..3a0edca 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "trek-client", - "version": "2.7.0", + "version": "2.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "trek-client", - "version": "2.7.0", + "version": "2.7.1", "dependencies": { "@react-pdf/renderer": "^4.3.2", "axios": "^1.6.7", @@ -2789,9 +2789,9 @@ } }, "node_modules/@rollup/pluginutils/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -3693,9 +3693,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4679,9 +4679,9 @@ "license": "MIT" }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { @@ -7181,9 +7181,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -8705,9 +8705,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { diff --git a/client/src/App.tsx b/client/src/App.tsx index a02662f..6a434f6 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -3,7 +3,6 @@ import { Routes, Route, Navigate, useLocation } from 'react-router-dom' import { useAuthStore } from './store/authStore' import { useSettingsStore } from './store/settingsStore' import LoginPage from './pages/LoginPage' -import RegisterPage from './pages/RegisterPage' import DashboardPage from './pages/DashboardPage' import TripPlannerPage from './pages/TripPlannerPage' import FilesPage from './pages/FilesPage' @@ -14,7 +13,6 @@ import AtlasPage from './pages/AtlasPage' import SharedTripPage from './pages/SharedTripPage' import { ToastContainer } from './components/shared/Toast' import { TranslationProvider, useTranslation } from './i18n' -import DemoBanner from './components/Layout/DemoBanner' import { authApi } from './api/client' interface ProtectedRouteProps { diff --git a/client/src/store/authStore.ts b/client/src/store/authStore.ts index 472d2d9..66206ee 100644 --- a/client/src/store/authStore.ts +++ b/client/src/store/authStore.ts @@ -29,7 +29,7 @@ interface AuthState { login: (email: string, password: string) => Promise completeMfaLogin: (mfaToken: string, code: string) => Promise - register: (username: string, email: string, password: string) => Promise + register: (username: string, email: string, password: string, invite_token?: string) => Promise logout: () => void /** Pass `{ silent: true }` to refresh the user without toggling global isLoading (avoids unmounting protected routes). */ loadUser: (opts?: { silent?: boolean }) => Promise @@ -126,6 +126,11 @@ export const useAuthStore = create((set, get) => ({ logout: () => { disconnect() localStorage.removeItem('auth_token') + // Clear service worker caches containing sensitive data + if ('caches' in window) { + caches.delete('api-data').catch(() => {}) + caches.delete('user-uploads').catch(() => {}) + } set({ user: null, token: null, @@ -151,13 +156,20 @@ export const useAuthStore = create((set, get) => ({ }) connect(token) } catch (err: unknown) { - localStorage.removeItem('auth_token') - set({ - user: null, - token: null, - isAuthenticated: false, - isLoading: false, - }) + // Only clear auth state on 401 (invalid/expired token), not on network errors + const isAuthError = err && typeof err === 'object' && 'response' in err && + (err as { response?: { status?: number } }).response?.status === 401 + if (isAuthError) { + localStorage.removeItem('auth_token') + set({ + user: null, + token: null, + isAuthenticated: false, + isLoading: false, + }) + } else { + set({ isLoading: false }) + } } }, diff --git a/client/vite.config.js b/client/vite.config.js index fbbe41e..453b6e1 100644 --- a/client/vite.config.js +++ b/client/vite.config.js @@ -45,23 +45,24 @@ export default defineConfig({ }, { // API calls — prefer network, fall back to cache - urlPattern: /\/api\/.*/i, + // Exclude sensitive endpoints (auth, admin, backup, settings) + urlPattern: /\/api\/(?!auth|admin|backup|settings).*/i, handler: 'NetworkFirst', options: { cacheName: 'api-data', expiration: { maxEntries: 200, maxAgeSeconds: 24 * 60 * 60 }, networkTimeoutSeconds: 5, - cacheableResponse: { statuses: [0, 200] }, + cacheableResponse: { statuses: [200] }, }, }, { - // Uploaded files (photos, covers, documents) - urlPattern: /\/uploads\/.*/i, + // Uploaded files (photos, covers — public assets only) + urlPattern: /\/uploads\/(?:covers|avatars)\/.*/i, handler: 'CacheFirst', options: { cacheName: 'user-uploads', - expiration: { maxEntries: 300, maxAgeSeconds: 30 * 24 * 60 * 60 }, - cacheableResponse: { statuses: [0, 200] }, + expiration: { maxEntries: 300, maxAgeSeconds: 7 * 24 * 60 * 60 }, + cacheableResponse: { statuses: [200] }, }, }, ], @@ -87,6 +88,9 @@ export default defineConfig({ }, }), ], + build: { + sourcemap: false, + }, server: { port: 5173, proxy: {