diff --git a/apps/pwa/index.html b/apps/pwa/index.html index e3cb868..5eca05e 100644 --- a/apps/pwa/index.html +++ b/apps/pwa/index.html @@ -4,6 +4,10 @@ + + + + FamilySync diff --git a/apps/pwa/public/apple-touch-icon.png b/apps/pwa/public/apple-touch-icon.png new file mode 100644 index 0000000..69ebb44 Binary files /dev/null and b/apps/pwa/public/apple-touch-icon.png differ diff --git a/apps/pwa/public/icon-192.png b/apps/pwa/public/icon-192.png new file mode 100644 index 0000000..ce6e1c8 Binary files /dev/null and b/apps/pwa/public/icon-192.png differ diff --git a/apps/pwa/public/icon-512.png b/apps/pwa/public/icon-512.png new file mode 100644 index 0000000..d7a82ef Binary files /dev/null and b/apps/pwa/public/icon-512.png differ diff --git a/apps/pwa/vite.config.ts b/apps/pwa/vite.config.ts index 0035c59..1a53218 100644 --- a/apps/pwa/vite.config.ts +++ b/apps/pwa/vite.config.ts @@ -1,8 +1,43 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import { VitePWA } from 'vite-plugin-pwa' export default defineConfig({ - plugins: [react()], + plugins: [ + react(), + VitePWA({ + registerType: 'autoUpdate', + // ⚠️ CRITICAL: exclude /callback from SW navigation handling (Gate 2 — T-03-20) + // The OIDC authorization-code exchange lands on /callback — if the SW + // intercepts this as a navigation, it serves the cached shell instead of + // letting the server process the authorization code exchange (login loop). + workbox: { + navigateFallback: '/index.html', + navigateFallbackDenylist: [ + /^\/callback/, // OIDC redirect endpoint — must reach the server + /^\/api\//, // API calls — never serve from cache + /^\/health/, // Health endpoint + ], + // No /api caching — runtimeCaching: [] means all API calls fall through to network + runtimeCaching: [], + }, + manifest: { + name: 'FamilySync', + short_name: 'FamilySync', + description: 'Family calendar and lists', + theme_color: '#4A90D9', + background_color: '#ffffff', + display: 'standalone', + scope: '/', + start_url: '/', + icons: [ + { src: '/icon-192.png', sizes: '192x192', type: 'image/png' }, + { src: '/icon-512.png', sizes: '512x512', type: 'image/png' }, + { src: '/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' }, + ], + }, + }), + ], server: { proxy: { '/health': 'http://localhost:3000',