memos/web/public/sw.js

95 lines
2.5 KiB
JavaScript

// Service Worker for Memos offline support
const CACHE_NAME = "memos-cache-v1";
const RUNTIME_CACHE = "memos-runtime-cache-v1";
// Assets to cache on install
const PRECACHE_ASSETS = [
"/",
"/logo.webp",
"/apple-touch-icon.png",
"/android-chrome-192x192.png",
"/android-chrome-512x512.png",
];
// Install event - cache essential assets
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(PRECACHE_ASSETS).catch((error) => {
console.error("Failed to cache assets during install:", error);
});
})
);
self.skipWaiting();
});
// Activate event - cleanup old caches
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME && cacheName !== RUNTIME_CACHE) {
return caches.delete(cacheName);
}
})
);
})
);
self.clients.claim();
});
// Fetch event - network first, fallback to cache
self.addEventListener("fetch", (event) => {
// Skip cross-origin requests
if (!event.request.url.startsWith(self.location.origin)) {
return;
}
// Skip non-GET requests
if (event.request.method !== "GET") {
return;
}
event.respondWith(
fetch(event.request)
.then((response) => {
// Don't cache if not a valid response
if (!response || response.status !== 200 || response.type === "error") {
return response;
}
// Clone the response
const responseToCache = response.clone();
// Cache the fetched response
caches.open(RUNTIME_CACHE).then((cache) => {
cache.put(event.request, responseToCache);
});
return response;
})
.catch(() => {
// Network failed, try cache
return caches.match(event.request).then((response) => {
if (response) {
return response;
}
// If not in cache and offline, return a basic offline page for navigation requests
if (event.request.mode === "navigate") {
return caches.match("/");
}
return new Response("Offline - resource not available", {
status: 503,
statusText: "Service Unavailable",
headers: new Headers({
"Content-Type": "text/plain",
}),
});
});
})
);
});