NoteJS/public/sw.js
Fovway 95401328c4 Улучшена поддержка PWA и мобильных устройств
- Обновлены инструкции по тестированию PWA для мобильных и десктопных устройств.
- Добавлены новые мета-теги и улучшены иконки для поддержки iOS и Windows.
- Оптимизирован Service Worker для кэширования и обработки ошибок.
- Реализована кнопка установки, отображающаяся только на мобильных устройствах, с различными инструкциями для разных браузеров.
- Обновлен manifest.json с добавлением категорий и скриншотов.
2025-10-20 09:35:32 +07:00

229 lines
6.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Service Worker для NoteJS
const APP_VERSION = '1.0.3';
const CACHE_NAME = `notejs-v${APP_VERSION}`;
const STATIC_CACHE_NAME = `notejs-static-v${APP_VERSION}`;
// Файлы для кэширования при установке
const STATIC_FILES = [
'/',
'/index.html',
'/style.css',
'/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',
'/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;
}
// Обрабатываем только 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) {
const cache = await caches.open(STATIC_CACHE_NAME);
cache.put(request, networkResponse.clone());
}
return networkResponse;
} catch (error) {
console.error('[SW] Ошибка при обработке запроса:', error);
// Fallback для HTML страниц
if (request.headers.get('accept')?.includes('text/html')) {
const fallbackResponse = await caches.match('/index.html');
if (fallbackResponse) {
return fallbackResponse;
}
}
// Fallback для иконок
if (request.url.includes('/icons/')) {
const fallbackIcon = await caches.match('/icons/icon-192x192.png');
if (fallbackIcon) {
return fallbackIcon;
}
}
// Fallback для CSS
if (request.url.includes('/style.css')) {
const fallbackCSS = await caches.match('/style.css');
if (fallbackCSS) {
return fallbackCSS;
}
}
throw error;
}
}
// Обработка сообщений от основного потока
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);
}
}