// Service Worker для NoteJS const APP_VERSION = "1.0.6"; const CACHE_NAME = `notejs-v${APP_VERSION}`; const STATIC_CACHE_NAME = `notejs-static-v${APP_VERSION}`; // Файлы для кэширования при установке (включая манифест для Brave) const STATIC_FILES = [ "/manifest.json", "/icons/icon-72x72.png", "/icons/icon-96x96.png", "/icons/icon-128x128.png", "/icons/icon-144x144.png", "/icons/icon-152x152.png", "/icons/icon-192x192.png", "/icons/icon-384x384.png", "/icons/icon-512x512.png", "/icons/icon-32x32.png", "/icons/icon-16x16.png", "/icons/icon-48x48.png", "/icon.svg", "/logo.svg", ]; // Установка Service Worker self.addEventListener("install", (event) => { console.log("[SW] Установка Service Worker"); event.waitUntil( caches .open(STATIC_CACHE_NAME) .then((cache) => { console.log("[SW] Кэширование статических файлов"); return cache.addAll(STATIC_FILES); }) .then(() => { console.log("[SW] Статические файлы закэшированы"); return self.skipWaiting(); }) .catch((error) => { console.error("[SW] Ошибка при кэшировании статических файлов:", error); // Продолжаем работу даже если кэширование не удалось return self.skipWaiting(); }) ); }); // Активация Service Worker self.addEventListener("activate", (event) => { console.log("[SW] Активация Service Worker"); event.waitUntil( caches .keys() .then((cacheNames) => { return Promise.all( cacheNames.map((cacheName) => { // Удаляем старые кэши if (cacheName !== STATIC_CACHE_NAME) { console.log("[SW] Удаление старого кэша:", cacheName); return caches.delete(cacheName); } }) ); }) .then(() => { console.log("[SW] Service Worker активирован"); return self.clients.claim(); }) ); }); // Перехват запросов self.addEventListener("fetch", (event) => { const { request } = event; const url = new URL(request.url); // Пропускаем запросы к API и загрузкам if ( url.pathname.startsWith("/api/") || url.pathname.startsWith("/uploads/") || url.pathname.startsWith("/database/") ) { return; } // Пропускаем запросы к внешним ресурсам if (url.origin !== location.origin) { return; } // Пропускаем HTML файлы и CSS - не кэшируем их if ( url.pathname.endsWith(".html") || url.pathname.endsWith(".css") || url.pathname === "/" || url.pathname === "/index.html" || url.pathname === "/login.html" || url.pathname === "/register.html" || url.pathname === "/notes.html" || url.pathname === "/profile.html" ) { return; } // Обрабатываем только GET запросы для изображений, иконок и манифеста if (request.method === "GET") { event.respondWith(handleRequest(request)); } }); async function handleRequest(request) { try { // Сначала пытаемся получить из кэша const cachedResponse = await caches.match(request); if (cachedResponse) { console.log("[SW] Запрос из кэша:", request.url); return cachedResponse; } // Если нет в кэше, загружаем из сети console.log("[SW] Запрос к сети:", request.url); const networkResponse = await fetch(request); // Кэшируем только изображения и иконки (без манифеста) if (networkResponse.ok && shouldCache(request)) { const cache = await caches.open(STATIC_CACHE_NAME); cache.put(request, networkResponse.clone()); } return networkResponse; } catch (error) { console.error("[SW] Ошибка при обработке запроса:", error); // НЕ предоставляем fallback - приложение работает только онлайн throw error; } } // Функция для определения, нужно ли кэшировать файл function shouldCache(request) { const url = new URL(request.url); // Кэшируем изображения, иконки, логотипы и манифест (для Brave совместимости) return ( url.pathname.includes("/icons/") || url.pathname.endsWith(".png") || url.pathname.endsWith(".jpg") || url.pathname.endsWith(".jpeg") || url.pathname.endsWith(".gif") || url.pathname.endsWith(".webp") || url.pathname.endsWith(".svg") || url.pathname.endsWith("manifest.json") ); } // Обработка сообщений от основного потока self.addEventListener("message", (event) => { console.log("[SW] Получено сообщение:", event.data); if (event.data && event.data.type === "SKIP_WAITING") { self.skipWaiting(); } if (event.data && event.data.type === "GET_VERSION") { event.ports[0].postMessage({ version: CACHE_NAME }); } if (event.data && event.data.type === "FORCE_UPDATE_CACHE") { forceUpdateCache(); } if (event.data && event.data.type === "CLEAR_ALL_CACHE") { clearAllCache(); } }); // Функция принудительного обновления кэша async function forceUpdateCache() { console.log("[SW] Принудительное обновление кэша..."); try { // Удаляем все старые кэши const cacheNames = await caches.keys(); await Promise.all( cacheNames.map((cacheName) => { console.log("[SW] Удаление кэша:", cacheName); return caches.delete(cacheName); }) ); // Создаем новый кэш с актуальной версией const newCache = await caches.open(STATIC_CACHE_NAME); await newCache.addAll(STATIC_FILES); console.log("[SW] Кэш успешно обновлен до версии:", APP_VERSION); // Уведомляем клиентов об обновлении const clients = await self.clients.matchAll(); clients.forEach((client) => { client.postMessage({ type: "CACHE_UPDATED", version: APP_VERSION, }); }); } catch (error) { console.error("[SW] Ошибка при обновлении кэша:", error); } } // Функция полной очистки кэша async function clearAllCache() { console.log("[SW] Полная очистка кэша..."); try { const cacheNames = await caches.keys(); await Promise.all( cacheNames.map((cacheName) => { console.log("[SW] Удаление кэша:", cacheName); return caches.delete(cacheName); }) ); console.log("[SW] Весь кэш очищен"); // Уведомляем клиентов об очистке const clients = await self.clients.matchAll(); clients.forEach((client) => { client.postMessage({ type: "CACHE_CLEARED", }); }); } catch (error) { console.error("[SW] Ошибка при очистке кэша:", error); } }