// BossAi Service Worker - 离线访问支持 // 策略:API 请求 network-first,静态资源 cache-first // 注意:开发环境(localhost:5173)不应注册 SW,index.html 已做端口判断 // 本文件仅在生产环境生效 const CACHE_VERSION = 'bossai-v2' const STATIC_CACHE = `${CACHE_VERSION}-static` const API_CACHE = `${CACHE_VERSION}-api` // 预缓存静态资源 const PRECACHE_URLS = [ '/', '/index.html', '/manifest.json', ] // 安装:预缓存 self.addEventListener('install', (event) => { event.waitUntil( caches.open(STATIC_CACHE).then((cache) => cache.addAll(PRECACHE_URLS)) .then(() => self.skipWaiting()) ) }) // 激活:清理旧缓存 self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((keys) => Promise.all( keys .filter((key) => !key.startsWith(CACHE_VERSION)) .map((key) => caches.delete(key)) ) ).then(() => self.clients.claim()) ) }) // 请求拦截 self.addEventListener('fetch', (event) => { const { request } = event const url = new URL(request.url) // 只处理同源请求 if (url.origin !== self.location.origin) return // API 请求:network-first,失败时返回缓存 if (url.pathname.startsWith('/api/')) { event.respondWith( fetch(request) .then((response) => { // 缓存成功的 GET 响应 if (request.method === 'GET' && response.ok) { const clone = response.clone() caches.open(API_CACHE).then((cache) => cache.put(request, clone)) } return response }) .catch(() => { // 网络失败,尝试缓存 if (request.method === 'GET') { return caches.match(request).then( (cached) => cached || new Response( JSON.stringify({ detail: '离线模式,数据不可用' }), { status: 503, headers: { 'Content-Type': 'application/json' } } ) ) } // 非 GET 请求离线时返回错误 return new Response( JSON.stringify({ detail: '离线模式,操作不可用' }), { status: 503, headers: { 'Content-Type': 'application/json' } } ) }) ) return } // 静态资源:cache-first,失败时网络 event.respondWith( caches.match(request).then((cached) => { if (cached) return cached return fetch(request).then((response) => { // 缓存成功的响应 if (response.ok && request.method === 'GET') { const clone = response.clone() caches.open(STATIC_CACHE).then((cache) => cache.put(request, clone)) } return response }) }) ) })