242 lines
7.9 KiB
JavaScript
242 lines
7.9 KiB
JavaScript
/**
|
|
* Service Worker for RS_system PWA
|
|
* Implements offline-first architecture with strategic caching
|
|
* Version: 1.0.0
|
|
*/
|
|
|
|
const CACHE_VERSION = 'rs-system-v1.0.0';
|
|
const STATIC_CACHE = `${CACHE_VERSION}-static`;
|
|
const DYNAMIC_CACHE = `${CACHE_VERSION}-dynamic`;
|
|
const API_CACHE = `${CACHE_VERSION}-api`;
|
|
|
|
// Critical resources to cache on install
|
|
const STATIC_ASSETS = [
|
|
'/',
|
|
'/Home/Index',
|
|
'/Colaboracion/Create',
|
|
'/Colaboracion/Index',
|
|
'/css/site.css',
|
|
'/css/bootstrap.min.css',
|
|
'/css/bootstrap-icons.min.css',
|
|
'/js/site.js',
|
|
'/js/colaboraciones-offline-db.js',
|
|
'/js/colaboraciones-sync.js',
|
|
'/lib/jquery/dist/jquery.min.js',
|
|
'/lib/bootstrap/dist/js/bootstrap.bundle.min.js',
|
|
'/manifest.json',
|
|
'/Assets/icon-192x192.png',
|
|
'/Assets/icon-512x512.png'
|
|
];
|
|
|
|
// Install event - cache static assets
|
|
self.addEventListener('install', (event) => {
|
|
console.log('[Service Worker] Installing...');
|
|
|
|
event.waitUntil(
|
|
caches.open(STATIC_CACHE)
|
|
.then((cache) => {
|
|
console.log('[Service Worker] Caching static assets');
|
|
return cache.addAll(STATIC_ASSETS);
|
|
})
|
|
.then(() => {
|
|
console.log('[Service Worker] Installation complete');
|
|
return self.skipWaiting(); // Activate immediately
|
|
})
|
|
.catch((error) => {
|
|
console.error('[Service Worker] Installation failed:', error);
|
|
})
|
|
);
|
|
});
|
|
|
|
// Activate event - clean up old caches
|
|
self.addEventListener('activate', (event) => {
|
|
console.log('[Service Worker] Activating...');
|
|
|
|
event.waitUntil(
|
|
caches.keys()
|
|
.then((cacheNames) => {
|
|
return Promise.all(
|
|
cacheNames
|
|
.filter((name) => {
|
|
// Delete old version caches
|
|
return name.startsWith('rs-system-') && name !== STATIC_CACHE && name !== DYNAMIC_CACHE && name !== API_CACHE;
|
|
})
|
|
.map((name) => {
|
|
console.log('[Service Worker] Deleting old cache:', name);
|
|
return caches.delete(name);
|
|
})
|
|
);
|
|
})
|
|
.then(() => {
|
|
console.log('[Service Worker] Activation complete');
|
|
return self.clients.claim(); // Take control immediately
|
|
})
|
|
);
|
|
});
|
|
|
|
// Fetch event - implement caching strategies
|
|
self.addEventListener('fetch', (event) => {
|
|
const { request } = event;
|
|
const url = new URL(request.url);
|
|
|
|
// Skip chrome extension and non-HTTP requests
|
|
if (!url.protocol.startsWith('http')) {
|
|
return;
|
|
}
|
|
|
|
// API requests - Network First, fallback to offline indicator
|
|
if (url.pathname.includes('/api/') ||
|
|
url.pathname.includes('/Colaboracion/Sync') ||
|
|
url.pathname.includes('/Colaboracion/BuscarMiembros') ||
|
|
url.pathname.includes('/Colaboracion/ObtenerUltimosPagos')) {
|
|
|
|
event.respondWith(networkFirstStrategy(request, API_CACHE));
|
|
return;
|
|
}
|
|
|
|
// POST requests - Network Only (never cache)
|
|
if (request.method === 'POST') {
|
|
event.respondWith(
|
|
fetch(request).catch(() => {
|
|
return new Response(
|
|
JSON.stringify({
|
|
success: false,
|
|
offline: true,
|
|
message: 'Sin conexión. Por favor intente más tarde.'
|
|
}),
|
|
{
|
|
headers: { 'Content-Type': 'application/json' },
|
|
status: 503
|
|
}
|
|
);
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Static assets - Cache First, fallback to Network
|
|
if (isStaticAsset(url.pathname)) {
|
|
event.respondWith(cacheFirstStrategy(request, STATIC_CACHE));
|
|
return;
|
|
}
|
|
|
|
// Dynamic content (HTML pages) - Network First, fallback to Cache
|
|
event.respondWith(networkFirstStrategy(request, DYNAMIC_CACHE));
|
|
});
|
|
|
|
/**
|
|
* Cache First Strategy
|
|
* Try cache first, fallback to network, then cache the response
|
|
*/
|
|
function cacheFirstStrategy(request, cacheName) {
|
|
return caches.match(request)
|
|
.then((cachedResponse) => {
|
|
if (cachedResponse) {
|
|
return cachedResponse;
|
|
}
|
|
|
|
return fetch(request)
|
|
.then((networkResponse) => {
|
|
// Clone the response
|
|
const responseToCache = networkResponse.clone();
|
|
|
|
caches.open(cacheName)
|
|
.then((cache) => {
|
|
cache.put(request, responseToCache);
|
|
});
|
|
|
|
return networkResponse;
|
|
})
|
|
.catch((error) => {
|
|
console.error('[Service Worker] Fetch failed:', error);
|
|
// Return offline page if available
|
|
return caches.match('/offline.html') || new Response('Offline');
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Network First Strategy
|
|
* Try network first, fallback to cache
|
|
*/
|
|
function networkFirstStrategy(request, cacheName) {
|
|
return fetch(request)
|
|
.then((networkResponse) => {
|
|
// Clone and cache the response
|
|
const responseToCache = networkResponse.clone();
|
|
|
|
caches.open(cacheName)
|
|
.then((cache) => {
|
|
cache.put(request, responseToCache);
|
|
});
|
|
|
|
return networkResponse;
|
|
})
|
|
.catch((error) => {
|
|
console.log('[Service Worker] Network failed, trying cache:', error);
|
|
|
|
return caches.match(request)
|
|
.then((cachedResponse) => {
|
|
if (cachedResponse) {
|
|
return cachedResponse;
|
|
}
|
|
|
|
// If API request and no cache, return offline indicator
|
|
if (request.url.includes('/api/') || request.url.includes('/Colaboracion/')) {
|
|
return new Response(
|
|
JSON.stringify({ offline: true }),
|
|
{
|
|
headers: { 'Content-Type': 'application/json' },
|
|
status: 503
|
|
}
|
|
);
|
|
}
|
|
|
|
throw error;
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Check if request is for a static asset
|
|
*/
|
|
function isStaticAsset(pathname) {
|
|
const staticExtensions = ['.css', '.js', '.jpg', '.jpeg', '.png', '.gif', '.svg', '.woff', '.woff2', '.ttf', '.eot', '.ico'];
|
|
return staticExtensions.some(ext => pathname.endsWith(ext));
|
|
}
|
|
|
|
// Background Sync for future enhancement
|
|
self.addEventListener('sync', (event) => {
|
|
console.log('[Service Worker] Background sync triggered:', event.tag);
|
|
|
|
if (event.tag === 'sync-colaboraciones') {
|
|
event.waitUntil(
|
|
// This will be handled by colaboraciones-sync.js
|
|
self.registration.showNotification('Sincronización completada', {
|
|
body: 'Las colaboraciones offline se han sincronizado exitosamente.',
|
|
icon: '/Assets/icon-192x192.png',
|
|
badge: '/Assets/icon-192x192.png'
|
|
})
|
|
);
|
|
}
|
|
});
|
|
|
|
// Message handler for cache updates
|
|
self.addEventListener('message', (event) => {
|
|
if (event.data && event.data.type === 'SKIP_WAITING') {
|
|
self.skipWaiting();
|
|
}
|
|
|
|
if (event.data && event.data.type === 'CLEAR_CACHE') {
|
|
event.waitUntil(
|
|
caches.keys().then((cacheNames) => {
|
|
return Promise.all(
|
|
cacheNames.map((cacheName) => caches.delete(cacheName))
|
|
);
|
|
})
|
|
);
|
|
}
|
|
});
|
|
|
|
console.log('[Service Worker] Loaded and ready');
|