// MQL5 Trading Automation Service Worker // Version: 1.0.0 const CACHE_NAME = 'mql5-automation-v1'; const RUNTIME_CACHE = 'mql5-runtime-v1'; // Assets to cache on install const PRECACHE_ASSETS = [ '/', '/index.html', '/dashboard/index.html', '/manifest.json', '/sw-inspector.html', '/offline.html' ]; // Install event - cache essential assets self.addEventListener('install', (event) => { console.log('[Service Worker] Installing...'); event.waitUntil( caches.open(CACHE_NAME) .then((cache) => { console.log('[Service Worker] Precaching assets'); return cache.addAll(PRECACHE_ASSETS); }) .then(() => { console.log('[Service Worker] Installation complete'); return self.skipWaiting(); }) .catch((error) => { console.error('[Service Worker] Installation failed:', error); }) ); }); // Activate event - clean up old caches self.addEventListener('activate', (event) => { console.log('[Service Worker] Activating...'); event.waitUntil( caches.keys() .then((cacheNames) => { return Promise.all( cacheNames .filter((cacheName) => { return cacheName !== CACHE_NAME && cacheName !== RUNTIME_CACHE; }) .map((cacheName) => { console.log('[Service Worker] Deleting old cache:', cacheName); return caches.delete(cacheName); }) ); }) .then(() => { console.log('[Service Worker] Activation complete'); return self.clients.claim(); }) ); }); // Fetch event - serve from cache, fallback to network self.addEventListener('fetch', (event) => { // Skip cross-origin requests if (!event.request.url.startsWith(self.location.origin)) { return; } // Network-first strategy for API requests if (event.request.url.includes('/api/')) { event.respondWith( fetch(event.request) .then((response) => { // Clone the response before caching const responseClone = response.clone(); caches.open(RUNTIME_CACHE).then((cache) => { cache.put(event.request, responseClone); }); return response; }) .catch(() => { return caches.match(event.request); }) ); return; } // Cache-first strategy for static assets event.respondWith( caches.match(event.request) .then((cachedResponse) => { if (cachedResponse) { // Return cached version and update cache in background fetch(event.request) .then((response) => { caches.open(RUNTIME_CACHE).then((cache) => { cache.put(event.request, response); }); }) .catch(() => { // Network request failed, but we have cache }); return cachedResponse; } // Not in cache, fetch from network return fetch(event.request) .then((response) => { // Cache successful responses if (response.status === 200) { const responseClone = response.clone(); caches.open(RUNTIME_CACHE).then((cache) => { cache.put(event.request, responseClone); }); } return response; }) .catch((error) => { console.error('[Service Worker] Fetch failed:', error); // Return offline page if available return caches.match('/offline.html') || new Response('Offline'); }); }) ); }); // Message event - handle commands from clients self.addEventListener('message', (event) => { console.log('[Service Worker] Message received:', event.data); if (event.data.type === 'SKIP_WAITING') { self.skipWaiting(); } if (event.data.type === 'CACHE_STATS') { caches.keys().then((cacheNames) => { Promise.all( cacheNames.map((cacheName) => { return caches.open(cacheName).then((cache) => { return cache.keys().then((keys) => { return { name: cacheName, size: keys.length }; }); }); }) ).then((stats) => { event.ports[0].postMessage({ type: 'CACHE_STATS_RESPONSE', data: stats }); }); }); } if (event.data.type === 'CLEAR_CACHE') { caches.keys().then((cacheNames) => { Promise.all( cacheNames.map((cacheName) => caches.delete(cacheName)) ).then(() => { event.ports[0].postMessage({ type: 'CACHE_CLEARED', success: true }); }); }); } }); // Push notification event self.addEventListener('push', (event) => { console.log('[Service Worker] Push received'); const options = { body: event.data ? event.data.text() : 'New trading notification', icon: '/icons/icon-192x192.png', badge: '/icons/icon-72x72.png', vibrate: [200, 100, 200], tag: 'trading-notification', requireInteraction: false }; event.waitUntil( self.registration.showNotification('MQL5 Trading Automation', options) ); }); // Notification click event self.addEventListener('notificationclick', (event) => { console.log('[Service Worker] Notification clicked'); event.notification.close(); event.waitUntil( clients.openWindow('/') ); });