- Updated Docker configuration for production deployment - Added SilverPay integration settings - Configured for admin.thebankofdebbie.giize.com deployment - Includes all recent security fixes and improvements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
192 lines
6.0 KiB
JavaScript
192 lines
6.0 KiB
JavaScript
// LittleShop Admin - Service Worker
|
|
// Provides offline functionality and app-like experience
|
|
|
|
const CACHE_NAME = 'littleshop-admin-v2';
|
|
const urlsToCache = [
|
|
// Only cache static assets, not dynamic admin pages
|
|
'/lib/bootstrap/css/bootstrap.min.css',
|
|
'/lib/fontawesome/css/all.min.css',
|
|
'/lib/bootstrap-icons/bootstrap-icons.css',
|
|
'/css/modern-admin.css',
|
|
'/lib/jquery/jquery.min.js',
|
|
'/lib/bootstrap/js/bootstrap.bundle.min.js',
|
|
'/js/modern-mobile.js',
|
|
'/js/pwa.js',
|
|
'/lib/fontawesome/webfonts/fa-solid-900.woff2',
|
|
'/lib/fontawesome/webfonts/fa-brands-400.woff2'
|
|
];
|
|
|
|
// Install event - cache resources
|
|
self.addEventListener('install', (event) => {
|
|
console.log('SW: Installing service worker');
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME)
|
|
.then((cache) => {
|
|
console.log('SW: Caching app shell');
|
|
return cache.addAll(urlsToCache);
|
|
})
|
|
);
|
|
});
|
|
|
|
// Activate event - clean up old caches
|
|
self.addEventListener('activate', (event) => {
|
|
console.log('SW: Activating service worker');
|
|
event.waitUntil(
|
|
caches.keys().then((cacheNames) => {
|
|
return Promise.all(
|
|
cacheNames.map((cacheName) => {
|
|
if (cacheName !== CACHE_NAME) {
|
|
console.log('SW: Deleting old cache:', cacheName);
|
|
return caches.delete(cacheName);
|
|
}
|
|
})
|
|
);
|
|
})
|
|
);
|
|
});
|
|
|
|
// Push event - handle incoming push notifications
|
|
self.addEventListener('push', (event) => {
|
|
console.log('SW: Push notification received');
|
|
|
|
if (!event.data) {
|
|
console.log('SW: Push notification received without data');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const data = event.data.json();
|
|
console.log('SW: Push notification data:', data);
|
|
|
|
const notificationOptions = {
|
|
body: data.body || 'New notification',
|
|
icon: data.icon || '/icons/icon-192x192.png',
|
|
badge: data.badge || '/icons/icon-72x72.png',
|
|
tag: 'littleshop-notification',
|
|
requireInteraction: false,
|
|
silent: false,
|
|
data: data.data || {}
|
|
};
|
|
|
|
event.waitUntil(
|
|
self.registration.showNotification(data.title || 'LittleShop Admin', notificationOptions)
|
|
);
|
|
} catch (error) {
|
|
console.error('SW: Error parsing push notification data:', error);
|
|
|
|
// Show generic notification
|
|
event.waitUntil(
|
|
self.registration.showNotification('LittleShop Admin', {
|
|
body: 'New notification received',
|
|
icon: '/icons/icon-192x192.png',
|
|
badge: '/icons/icon-72x72.png',
|
|
tag: 'littleshop-notification'
|
|
})
|
|
);
|
|
}
|
|
});
|
|
|
|
// Notification click event - handle user interaction with notifications
|
|
self.addEventListener('notificationclick', (event) => {
|
|
console.log('SW: Notification clicked');
|
|
event.notification.close();
|
|
|
|
const notificationData = event.notification.data || {};
|
|
|
|
// Determine URL to open
|
|
let urlToOpen = '/Admin/Dashboard';
|
|
if (notificationData.url) {
|
|
urlToOpen = notificationData.url;
|
|
} else if (notificationData.orderId) {
|
|
urlToOpen = `/Admin/Orders/Details/${notificationData.orderId}`;
|
|
}
|
|
|
|
event.waitUntil(
|
|
clients.matchAll({ type: 'window', includeUncontrolled: true })
|
|
.then((clientList) => {
|
|
// Check if there's already a window/tab open with the target URL
|
|
for (const client of clientList) {
|
|
if (client.url.includes(urlToOpen) && 'focus' in client) {
|
|
return client.focus();
|
|
}
|
|
}
|
|
|
|
// If no window/tab is already open, open a new one
|
|
if (clients.openWindow) {
|
|
return clients.openWindow(urlToOpen);
|
|
}
|
|
})
|
|
);
|
|
});
|
|
|
|
// Fetch event - network-first for admin pages, cache-first for assets
|
|
self.addEventListener('fetch', (event) => {
|
|
// Only handle GET requests
|
|
if (event.request.method !== 'GET') {
|
|
return;
|
|
}
|
|
|
|
const url = new URL(event.request.url);
|
|
|
|
// Network-first strategy for admin pages (always fresh data)
|
|
if (url.pathname.startsWith('/Admin/')) {
|
|
event.respondWith(
|
|
fetch(event.request).catch(() => {
|
|
// If network fails, try cache as fallback
|
|
return caches.match(event.request);
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Cache-first strategy for static assets
|
|
event.respondWith(
|
|
caches.match(event.request)
|
|
.then((response) => {
|
|
// Return cached version or fetch from network
|
|
return response || fetch(event.request).catch(() => {
|
|
// If both cache and network fail, show offline page for HTML requests
|
|
if (event.request.headers.get('accept').includes('text/html')) {
|
|
return new Response(`
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>LittleShop Admin - Offline</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<style>
|
|
body {
|
|
font-family: system-ui, sans-serif;
|
|
text-align: center;
|
|
padding: 2rem;
|
|
background: #f9fafb;
|
|
color: #374151;
|
|
}
|
|
.offline-icon { font-size: 4rem; margin-bottom: 1rem; color: #6b7280; }
|
|
h1 { color: #1f2937; margin-bottom: 0.5rem; }
|
|
p { color: #6b7280; margin-bottom: 2rem; }
|
|
.btn {
|
|
background: #2563eb;
|
|
color: white;
|
|
padding: 0.75rem 1.5rem;
|
|
border: none;
|
|
border-radius: 0.5rem;
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="offline-icon">📱</div>
|
|
<h1>You're offline</h1>
|
|
<p>Please check your internet connection and try again.</p>
|
|
<a href="/Admin/Dashboard" class="btn">Try Again</a>
|
|
</body>
|
|
</html>
|
|
`, {
|
|
headers: { 'Content-Type': 'text/html' }
|
|
});
|
|
}
|
|
});
|
|
})
|
|
);
|
|
}); |