chore: sync local changes (2026-03-12)

This commit is contained in:
2026-03-12 18:58:26 +08:00
parent 04a4cb962a
commit 939442e061
348 changed files with 91638 additions and 92091 deletions

View File

@@ -1,48 +1,150 @@
// Service Worker for InfoGenie App
const CACHE_NAME = 'infogenie-cache-v1';
const urlsToCache = [
// Service Worker for InfoGenie App (PWA)
// 注意PWA 必须在 HTTPS或 localhost下才能生效
const CACHE_VERSION = 'v2';
const PRECACHE_NAME = `infogenie-precache-${CACHE_VERSION}`;
const RUNTIME_NAME = `infogenie-runtime-${CACHE_VERSION}`;
const CORE_ASSETS = [
'/',
'/index.html',
'/manifest.json'
'/manifest.json',
'/icons/icon-192.png',
'/icons/icon-512.png',
'/icons/icon-192-maskable.png',
'/icons/icon-512-maskable.png',
'/icons/apple-touch-icon.png',
'/icons/favicon-32.png',
'/icons/favicon-16.png'
];
// 安装Service Worker
async function safeAddAll(cache, urls) {
const uniqueUrls = Array.from(new Set(urls)).filter(Boolean);
const results = await Promise.allSettled(uniqueUrls.map(url => cache.add(url)));
const failures = results.filter(r => r.status === 'rejected');
if (failures.length > 0) {
console.warn('[SW] Some assets failed to cache:', failures.length);
}
}
async function precacheEntrypoints(cache) {
try {
const res = await fetch('/asset-manifest.json', { cache: 'no-store' });
if (!res.ok) return;
const manifest = await res.json();
const filesObj = manifest && typeof manifest === 'object' ? manifest.files : undefined;
const files = filesObj && typeof filesObj === 'object' ? Object.values(filesObj) : [];
const entrypoints = Array.isArray(manifest.entrypoints) ? manifest.entrypoints : [];
const urls = [...files, ...entrypoints]
.filter(p => typeof p === 'string')
.map(p => (p.startsWith('/') ? p : `/${p}`))
.filter(p => !p.endsWith('.map'));
await safeAddAll(cache, urls);
} catch (err) {
console.warn('[SW] Failed to precache entrypoints:', err);
}
}
self.addEventListener('install', event => {
self.skipWaiting();
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
(async () => {
const cache = await caches.open(PRECACHE_NAME);
await safeAddAll(cache, CORE_ASSETS);
await precacheEntrypoints(cache);
})()
);
});
// 拦截请求并从缓存中响应
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// 如果找到缓存的响应,则返回缓存
if (response) {
return response;
}
return fetch(event.request);
})
);
});
// 更新Service Worker
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
(async () => {
const cacheNames = await caches.keys();
await Promise.all(
cacheNames.map(name => {
if (name !== PRECACHE_NAME && name !== RUNTIME_NAME) {
return caches.delete(name);
}
return undefined;
})
);
})
await self.clients.claim();
})()
);
});
});
function isNavigationRequest(request) {
return request.mode === 'navigate' || request.destination === 'document';
}
function shouldHandleRequest(url, request) {
if (request.method !== 'GET') return false;
if (url.origin !== self.location.origin) return false;
return true;
}
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (!shouldHandleRequest(url, event.request)) return;
// 不缓存后端 API如需缓存请在这里加规则
if (url.pathname.startsWith('/api')) return;
// 页面请求:优先网络,离线回退到缓存
if (isNavigationRequest(event.request)) {
event.respondWith(
(async () => {
try {
const networkResponse = await fetch(event.request);
const cache = await caches.open(RUNTIME_NAME);
cache.put(event.request, networkResponse.clone());
return networkResponse;
} catch (err) {
const cached = await caches.match(event.request);
return cached || caches.match('/index.html');
}
})()
);
return;
}
// 静态资源stale-while-revalidate
event.respondWith(
(async () => {
const cached = await caches.match(event.request);
const cache = await caches.open(RUNTIME_NAME);
const networkFetch = (async () => {
try {
const response = await fetch(event.request);
if (response && response.ok) {
cache.put(event.request, response.clone());
}
return response;
} catch (err) {
return undefined;
}
})();
if (cached) {
event.waitUntil(networkFetch);
return cached;
}
const response = await networkFetch;
if (response) return response;
return new Response('', { status: 504, statusText: 'Offline' });
})()
);
});
self.addEventListener('message', event => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});