From 08e8e1f7d3263f5aa399b892726c283d1826188a Mon Sep 17 00:00:00 2001 From: josedario87 Date: Tue, 28 Oct 2025 10:33:38 -0600 Subject: [PATCH] Solucionar problemas de PWA y cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agregar manifest.json a rutas públicas de Traefik (anteriormente solo estaba manifest.webmanifest) - Cambiar estrategia de cache del Service Worker de cache-first a estrategia inteligente: * Archivos de build (/assets/): Network-first con cache fallback * HTML: Network-first siempre para obtener última versión * Recursos estáticos (iconos, manifest): Cache-first (no cambian) - Incrementar versión de cache de v2 a v3 para forzar limpieza - Evitar redirecciones a Authentik para archivos manifest Esto soluciona: - Error CORS en manifest.json (ya no redirige a Authentik) - Problemas de cache que requerían limpiar datos del navegador - Archivos desactualizados servidos desde cache --- docker-compose.yml | 2 +- frontend/public/sw.js | 58 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index eb0ec4c..f157ab8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,7 +37,7 @@ services: # Router 1: Público (assets, manifest, icons) - SIN autenticación - ALTA PRIORIDAD # NOTA: /outpost.goauthentik.io NO debe estar aquí, lo maneja el middleware de Authentik - - "traefik.http.routers.wifi-nucleoriofrio-public.rule=Host(`wifi.nucleoriofrio.com`) && (PathPrefix(`/assets`) || PathPrefix(`/.well-known`) || PathPrefix(`/icons`) || Path(`/manifest.webmanifest`) || Path(`/favicon.ico`) || Path(`/vite.svg`) || Path(`/sw.js`))" + - "traefik.http.routers.wifi-nucleoriofrio-public.rule=Host(`wifi.nucleoriofrio.com`) && (PathPrefix(`/assets`) || PathPrefix(`/.well-known`) || PathPrefix(`/icons`) || Path(`/manifest.webmanifest`) || Path(`/manifest.json`) || Path(`/favicon.ico`) || Path(`/vite.svg`) || Path(`/sw.js`))" - "traefik.http.routers.wifi-nucleoriofrio-public.entrypoints=websecure" - "traefik.http.routers.wifi-nucleoriofrio-public.tls.certresolver=letsencrypt" - "traefik.http.routers.wifi-nucleoriofrio-public.service=wifi-nucleoriofrio-service" diff --git a/frontend/public/sw.js b/frontend/public/sw.js index 317e2b3..cfe1c27 100644 --- a/frontend/public/sw.js +++ b/frontend/public/sw.js @@ -1,5 +1,5 @@ // Nombre del caché para la aplicación -const CACHE_NAME = 'radius-nucleo-cache-v2'; +const CACHE_NAME = 'radius-nucleo-cache-v3'; // Archivos que se almacenarán en caché durante la instalación const URLS_TO_CACHE = [ @@ -42,21 +42,67 @@ self.addEventListener('activate', (event) => { ); }); -// Evento de recuperación (fetch): responde desde la caché si existe, si no, recupera de la red +// Evento de recuperación (fetch): estrategia inteligente según el tipo de recurso self.addEventListener('fetch', (event) => { + const url = new URL(event.request.url); + // Excluir las peticiones a /api/* del Service Worker // Estas peticiones requieren autenticación y no deben ser cacheadas - const url = new URL(event.request.url); if (url.pathname.startsWith('/api/')) { - // Dejar que el navegador maneje las peticiones de API normalmente return; } + // Para archivos de build (JS/CSS en /assets/): Network-first con cache fallback + // Esto asegura que siempre se intente obtener la versión más reciente + if (url.pathname.startsWith('/assets/')) { + event.respondWith( + fetch(event.request) + .then((response) => { + // Guardar en cache la nueva versión + const responseClone = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseClone); + }); + return response; + }) + .catch(() => { + // Si falla la red, usar cache + return caches.match(event.request); + }) + ); + return; + } + + // Para HTML: Network-first siempre + if (url.pathname.endsWith('.html') || url.pathname === '/') { + event.respondWith( + fetch(event.request) + .catch(() => { + // Solo si falla la red, usar cache + return caches.match(event.request); + }) + ); + return; + } + + // Para recursos estáticos (iconos, manifest, etc.): Cache-first + // Estos archivos rara vez cambian event.respondWith( caches.match(event.request) .then((response) => { - // Devuelve la respuesta en caché o realiza la solicitud a la red - return response || fetch(event.request); + if (response) { + return response; + } + return fetch(event.request).then((response) => { + // Guardar en cache si es exitoso + if (response.status === 200) { + const responseClone = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseClone); + }); + } + return response; + }); }) ); }); \ No newline at end of file