05037e7491
a drop outside of the browser will: - upload the file if only 1 file selected - upload a .zip of the directory or multiple selection (browsers do not permit multiple upload yet) note: I had to create a new handler because a post request to /api/batch/download is possible via JS but it will create a memory blob in the browser, during the drag action. This may exhaust the browser's memory if heavy files This will initiate zip creation from the server even if drop is canceled The best approach is to add a handler supporting GET calls, this call will be triggered by the browser on drop action outside of it's window
122 lines
4.3 KiB
JavaScript
122 lines
4.3 KiB
JavaScript
// OxiCloud Service Worker
|
|
// FIXME: generate cache name according build ?
|
|
const CACHE_NAME = 'oxicloud-cache-v21';
|
|
|
|
// Only cache static assets — NOT HTML files.
|
|
// HTML files are served network-first so browsers always get the latest
|
|
// script/link references. Caching HTML causes stale entry points.
|
|
const ASSETS_TO_CACHE = [
|
|
'/js/core/icons.js',
|
|
'/js/core/i18n.js',
|
|
'/js/core/languageSelector.js',
|
|
'/js/core/notifications.js',
|
|
'/locales/en.json',
|
|
'/locales/es.json',
|
|
'/locales/fa.json',
|
|
'/locales/de.json',
|
|
'/locales/nl.json',
|
|
'/favicon.ico'
|
|
];
|
|
|
|
// HTML paths that should always be fetched from the network
|
|
const HTML_PATHS = ['/', '/index.html', '/login', '/login.html', '/admin', '/admin.html', '/profile', '/profile.html'];
|
|
|
|
// Install event - cache assets
|
|
self.addEventListener('install', (event) => {
|
|
event.waitUntil(
|
|
caches
|
|
.open(CACHE_NAME)
|
|
.then((cache) => {
|
|
console.log('Cache opened');
|
|
return cache.addAll(ASSETS_TO_CACHE);
|
|
})
|
|
.then(() => self.skipWaiting()) // Activate immediately
|
|
);
|
|
});
|
|
|
|
// Activate event - clean old caches
|
|
self.addEventListener('activate', (event) => {
|
|
event.waitUntil(
|
|
caches
|
|
.keys()
|
|
.then((cacheNames) => {
|
|
return Promise.all(
|
|
cacheNames
|
|
.filter((cacheName) => {
|
|
return cacheName !== CACHE_NAME;
|
|
})
|
|
.map((cacheName) => {
|
|
return caches.delete(cacheName);
|
|
})
|
|
);
|
|
})
|
|
.then(() => self.clients.claim()) // Take control of clients
|
|
);
|
|
});
|
|
|
|
// Fetch event - serve from cache, update cache from network
|
|
self.addEventListener('fetch', (event) => {
|
|
// Don't intercept API requests - let them go straight to the network
|
|
if (event.request.url.includes('/api/')) {
|
|
return;
|
|
}
|
|
|
|
// HTML pages: always network-first so entry points are never stale
|
|
const pathname = new URL(event.request.url).pathname;
|
|
const isHtml = HTML_PATHS.includes(pathname) || pathname.endsWith('.html') || event.request.headers.get('accept')?.includes('text/html');
|
|
if (isHtml) {
|
|
event.respondWith(fetch(event.request).catch(() => caches.match(event.request)));
|
|
return;
|
|
}
|
|
|
|
event.respondWith(
|
|
caches.match(event.request).then((response) => {
|
|
// Cache hit - return the response from the cached version
|
|
if (response) {
|
|
// For non-core assets, still fetch from network for updates
|
|
if (!ASSETS_TO_CACHE.includes(new URL(event.request.url).pathname)) {
|
|
fetch(event.request)
|
|
.then((networkResponse) => {
|
|
if (networkResponse && networkResponse.status === 200) {
|
|
const clonedResponse = networkResponse.clone();
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
cache.put(event.request, clonedResponse);
|
|
});
|
|
}
|
|
})
|
|
.catch(() => {
|
|
// Ignore network fetch errors - we already have a cached version
|
|
});
|
|
}
|
|
return response;
|
|
}
|
|
|
|
// Not in cache - get from network and add to cache
|
|
return fetch(event.request).then((response) => {
|
|
if (!response || response.status !== 200 || response.type !== 'basic') {
|
|
return response;
|
|
}
|
|
|
|
// Clone the response as it's a stream and can only be consumed once
|
|
const responseToCache = response.clone();
|
|
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
cache.put(event.request, responseToCache);
|
|
});
|
|
|
|
return response;
|
|
});
|
|
})
|
|
);
|
|
});
|
|
|
|
// Background sync for failed requests
|
|
self.addEventListener('sync', (event) => {
|
|
if (event.tag === 'oxicloud-sync') {
|
|
event.waitUntil(
|
|
// Implement background sync for pending file operations
|
|
Promise.resolve() // Placeholder for actual implementation
|
|
);
|
|
}
|
|
});
|