PageSourceSearch

https://fittin.ru/sw.js

js fittin.ru collected 2026-10-03 20:15:39 UTC 9,790 bytes, 211 lines download raw bytes

1// Service Worker для оптимизированного кеширования ресурсов
2//
3// Версия кеша: при правке ЛЮБОГО файла из CRITICAL_RESOURCES или критичной
4// логики SW поднять SW_VERSION на 1. Это триггерит install → activate,
5// activate сносит все каши с другим именем, install заново пре-кеширует
6// CRITICAL_RESOURCES свежими. Одна точка правки вместо трёх захардкоженных.
7//
8// ВНИМАНИЕ: файлы шапки сайта (/css/header.css, /js/header-include.js)
9// намеренно ИСКЛЮЧЕНЫ из CRITICAL_RESOURCES и обрабатываются стратегией
10// network-only (см. HEADER_PATTERNS ниже). Сервер отдаёт их с Cache-Control:
11// no-cache, must-revalidate. Раньше они пре-кешировались здесь как bare-URL
12// без ?v=..., замораживались в STATIC_CACHE до бампа версии и при любом
13// fallback (caches.match) ломали вёрстку. Теперь правка шапки больше НЕ
14// требует бамп SW_VERSION — она течёт напрямую с сервера через 304.
15const SW_VERSION = 'v5';
16const CACHE_NAME = 'fittin-' + SW_VERSION;
17const STATIC_CACHE = 'fittin-static-' + SW_VERSION;
18const DYNAMIC_CACHE = 'fittin-dynamic-' + SW_VERSION;
19
20// Критически важные ресурсы для офлайн работы
21const CRITICAL_RESOURCES = [
22    '/',
23    '/main.html',
24    '/AIPhotoEditor.html',
25    '/css/fonts.css',
26    '/css/hero-block.css',
27    '/css/main.css',
28    '/css/ai-photo-editor.css',
29    '/js/site-config.js',
30    '/fonts/Gilroy-Regular.woff',
31    '/fonts/Gilroy-SemiBold.woff',
32    '/fonts/Gilroy-Bold.woff',
33    '/images/LogoFittin.webp',
34    '/images/Background-1-block.webp',
35    '/images/Novii.webp'
36];
37
38// Шапка сайта — всегда из сети, мимо любого кеша SW.
39// Пути сопоставляются с pathname (без query string).
40const HEADER_PATTERNS = [
41    /^\/css\/header\.css$/,
42    /^\/js\/header-include\.js$/
43];
44
45// Ресурсы, которые кешируются долго (статические)
46const LONG_CACHE_PATTERNS = [
47    /\.(woff|woff2|ttf|eot|otf)$/,  // Шрифты
48    /\.(jpg|jpeg|png|gif|webp|svg|ico)$/,  // Изображения
49    /\.(mp4|webm|avi|mov)$/  // Видео
50];
51
52// Ресурсы, которые кешируются средне (обновляемые)
53const MEDIUM_CACHE_PATTERNS = [
54    /\.(css|js)$/,  // CSS и JS
55    /\.(pdf|doc|docx)$/  // Документы
56];
57
58// Максимальный размер динамического кеша
59const MAX_DYNAMIC_CACHE_SIZE = 50;
60
61// Функция для очистки старого кеша
62const cleanupCache = async (cacheName, maxSize) => {
63    const cache = await caches.open(cacheName);
64    const keys = await cache.keys();
65    if (keys.length > maxSize) {
66        const keysToDelete = keys.slice(0, keys.length - maxSize);
67        await Promise.all(keysToDelete.map(key => cache.delete(key)));
68    }
69};
70
71// Установка Service Worker
72self.addEventListener('install', event => {
73    event.waitUntil(
74        caches.open(STATIC_CACHE)
75            .then(cache => {
76                return cache.addAll(CRITICAL_RESOURCES);
77            })
78    );
79    self.skipWaiting();
80});
81
82// Активация Service Worker
83self.addEventListener('activate', event => {
84    event.waitUntil(
85        Promise.all([
86            // Удаляем старые кеши
87            caches.keys().then(cacheNames => {
88                return Promise.all(
89                    cacheNames.map(cacheName => {
90                        if (cacheName !== STATIC_CACHE && cacheName !== DYNAMIC_CACHE) {
91                            return caches.delete(cacheName);
92                        }
93                    })
94                );
95            }),
96            // Очищаем динамический кеш если он слишком большой
97            cleanupCache(DYNAMIC_CACHE, MAX_DYNAMIC_CACHE_SIZE)
98        ])
99    );
100    self.clients.claim();
101});
102
103// Определяем стратегию кеширования для URL.
104// ВАЖНО: работаем с pathname (без query string). Старый код тестировал regex
105// против полного url вида "https://fittin.ru/css/header.css?v=3.3" — паттерн
106// /\.(css|js)$/ не матчил (URL заканчивается на "?v=3.3"), CSS/JS проваливались
107// в default-ветку cache-first и при первом запросе кешировались навсегда.
108const getCacheStrategy = (url) => {
109    let pathname = url;
110    try {
111        pathname = new URL(url).pathname;
112    } catch (_e) {
113        // url мог прийти без origin — оставим как есть, паттерны работают и так
114    }
115
116    // Шапка сайта — никогда не кешируется в SW. Сервер сам отдаёт 304 через ETag.
117    if (HEADER_PATTERNS.some(pattern => pattern.test(pathname))) {
118        return 'network-only';
119    }
120
121    // API запросы - всегда сеть
122    if (pathname.includes('/api/') || pathname.includes('/news/') || pathname.includes('/sync/')) {
123        return 'network-only';
124    }
125
126    // Статические ресурсы с долгим кешированием
127    if (LONG_CACHE_PATTERNS.some(pattern => pattern.test(pathname))) {
128        return 'cache-first';
129    }
130
131    // CSS/JS - сеть с fallback на кеш
132    if (MEDIUM_CACHE_PATTERNS.some(pattern => pattern.test(pathname))) {
133        return 'network-first';
134    }
135
136    // HTML и ЧПУ без расширения (напр. /all_cases, /o-nas): иначе срабатывал бы cache-first ниже —
137    // вечный старый документ после первого визита. Расширения ресурсов уже отфильтрованы выше.
138    if (pathname.endsWith('.html') || pathname.endsWith('/')) {
139        return 'network-first';
140    }
141    const lastSeg = pathname.split('/').filter(Boolean).pop() || '';
142    if (!lastSeg.includes('.')) {
143        return 'network-first';
144    }
145
146    return 'cache-first';
147};
148
149// Обработка запросов
150self.addEventListener('fetch', event => {
151    // Кэшируем только GET запросы
152    if (event.request.method !== 'GET') {
153        return;
154    }
155
156    const strategy = getCacheStrategy(event.request.url);
157    
158    if (strategy === 'network-only') {
159        // Шапка сайта и API: НЕ перехватываем запрос — отдаём его браузеру как есть.
160        // Раньше тут было event.respondWith(fetch(event.request)) без .catch(): при любом
161        // реджекте сети fetch падал с "Failed to fetch" (sw.js:160), respondWith получал
162        // отклонённый промис → браузер отдавал network error на /js/header-include.js, и
163        // шапка не рендерилась (а чистка кэша не помогала — SW перехватывал, но не кешировал).
164        // Без respondWith() браузер делает свой штатный fetch (HTTP-кеш, 304, редиректы) —
165        // он надёжнее и не воспроизводит этот реджект. Та же цель «мимо SW-кеша», без отказа.
166        return;
167    }
168
169    if (strategy === 'cache-first') {
170        // Сначала кеш, потом сеть
171        event.respondWith(
172            caches.match(event.request)
173                .then(response => {
174                    if (response) {
175                        return response;
176                    }
177                    
178                    return fetch(event.request)
179                        .then(fetchResponse => {
180                            if (fetchResponse && fetchResponse.status === 200) {
181                                const responseToCache = fetchResponse.clone();
182                                caches.open(STATIC_CACHE)
183                                    .then(cache => cache.put(event.request, responseToCache));
184                            }
185                            return fetchResponse;
186                        });
187                })
188        );
189    } else if (strategy === 'network-first') {
190        // Сначала сеть, потом кеш
191        event.respondWith(
192            fetch(event.request)
193                .then(response => {
194                    if (response && response.status === 200) {
195                        const responseToCache = response.clone();
196                        caches.open(DYNAMIC_CACHE)
197                            .then(cache => {
198                                cache.put(event.request, responseToCache);
199                                // Очищаем кеш при необходимости
200                                cleanupCache(DYNAMIC_CACHE, MAX_DYNAMIC_CACHE_SIZE);
201                            });
202                    }
203                    return response;
204                })
205                .catch(() => {
206                    // Fallback на кеш при ошибке сети
207                    return caches.match(event.request);
208                })
209        );
210    }
211});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.