CRM/frontend/public/service-worker.js

113 خطوط
2.9 KiB
JavaScript

const VERSION = 'v4'
const SHELL_CACHE = `crm-agent-shell-${VERSION}`
const STATIC_CACHE = `crm-agent-static-${VERSION}`
const API_CACHE = `crm-agent-api-${VERSION}`
const OFFLINE_URL = '/agent-mobile-offline.html'
const APP_SHELL = [
OFFLINE_URL,
'/manifest.json',
'/favicon.svg',
'/icons/icon-192.png',
'/icons/icon-512.png',
]
const CACHEABLE_API_PREFIXES = [
'/api/agent/mobile/bootstrap',
'/api/agent/mobile/dashboard',
'/api/agent/mobile/call-queue',
'/api/agent/mobile/leads',
'/api/agent/mobile/follow-ups',
]
const BLOCKED_PATHS = ['/storage/', '/uploads/', '/sanctum']
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(SHELL_CACHE)
.then((cache) => cache.addAll(APP_SHELL))
.then(() => self.skipWaiting())
.catch(() => undefined)
)
})
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys()
.then((keys) => Promise.all(keys
.filter((key) => ![SHELL_CACHE, STATIC_CACHE, API_CACHE].includes(key))
.map((key) => caches.delete(key))
))
.then(() => self.clients.claim())
)
})
self.addEventListener('message', (event) => {
if (event.data?.type === 'SKIP_WAITING') {
self.skipWaiting()
}
})
function isBlocked(url) {
return BLOCKED_PATHS.some((path) => url.pathname.startsWith(path)) || url.pathname.includes('recording')
}
function isMobileApi(url) {
return CACHEABLE_API_PREFIXES.some((path) => url.pathname.startsWith(path))
}
async function networkFirst(request, cacheName) {
const cache = await caches.open(cacheName)
try {
const response = await fetch(request)
if (response && response.ok) {
cache.put(request, response.clone())
}
return response
} catch (error) {
const cached = await cache.match(request)
if (cached) return cached
throw error
}
}
async function staleWhileRevalidate(request, cacheName) {
const cache = await caches.open(cacheName)
const cached = await cache.match(request)
const fetched = fetch(request).then((response) => {
if (response && response.ok && response.type !== 'opaque') {
cache.put(request, response.clone())
}
return response
}).catch(() => cached)
return cached || fetched
}
self.addEventListener('fetch', (event) => {
const request = event.request
const url = new URL(request.url)
if (request.method !== 'GET' || isBlocked(url)) return
if (request.mode === 'navigate') {
event.respondWith(
fetch(request).catch(() => {
if (url.pathname.startsWith('/agent-mobile')) {
return caches.match(OFFLINE_URL)
}
return Response.error()
})
)
return
}
if (isMobileApi(url)) {
event.respondWith(networkFirst(request, API_CACHE))
return
}
if (['script', 'style', 'font', 'image', 'manifest'].includes(request.destination)) {
event.respondWith(staleWhileRevalidate(request, STATIC_CACHE))
}
})