proper productionize project
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
/* __PWA_BUILD_ID__ is replaced by the Vite build before this file is emitted. */
|
||||
const buildId = '__PWA_BUILD_ID__'
|
||||
const cacheName = `lxc-control-v2-shell-${buildId}`
|
||||
const shellPath = '/control/'
|
||||
const shellRoutes = [
|
||||
'/control/',
|
||||
'/control/invitations',
|
||||
'/control/login',
|
||||
'/control/register',
|
||||
'/control/setup',
|
||||
]
|
||||
const stableAssets = [
|
||||
'/control/manifest.webmanifest',
|
||||
'/pwa/icon-192.png',
|
||||
'/pwa/icon-512.png',
|
||||
'/pwa/icon-maskable-512.png',
|
||||
'/pwa/apple-touch-icon.png',
|
||||
]
|
||||
|
||||
async function precacheShell() {
|
||||
const cache = await caches.open(cacheName)
|
||||
const response = await fetch(shellPath, { cache: 'reload', credentials: 'same-origin' })
|
||||
if (!response.ok) throw new Error(`Cannot precache control shell: ${response.status}`)
|
||||
|
||||
const html = await response.clone().text()
|
||||
await Promise.all(shellRoutes.map(route => cache.put(route, response.clone())))
|
||||
const generatedAssets = [...html.matchAll(/(?:src|href)="(\/assets\/[^"?#]+)"/g)]
|
||||
.map(match => match[1])
|
||||
await cache.addAll([...new Set([...stableAssets, ...generatedAssets])])
|
||||
}
|
||||
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(precacheShell())
|
||||
})
|
||||
|
||||
self.addEventListener('activate', event => {
|
||||
event.waitUntil((async () => {
|
||||
const names = await caches.keys()
|
||||
await Promise.all(names
|
||||
.filter(name => name.startsWith('lxc-control-v2-shell-') && name !== cacheName)
|
||||
.map(name => caches.delete(name)))
|
||||
await self.clients.claim()
|
||||
})())
|
||||
})
|
||||
|
||||
self.addEventListener('message', event => {
|
||||
if (event.data?.type === 'SKIP_WAITING') event.waitUntil(self.skipWaiting())
|
||||
})
|
||||
|
||||
function isControlShell(pathname) {
|
||||
return pathname.startsWith('/control/')
|
||||
}
|
||||
|
||||
function isStaticAsset(pathname) {
|
||||
return pathname.startsWith('/assets/')
|
||||
|| pathname.startsWith('/pwa/')
|
||||
|| pathname === '/control/manifest.webmanifest'
|
||||
}
|
||||
|
||||
async function navigationResponse(request) {
|
||||
const cache = await caches.open(cacheName)
|
||||
const pathname = new URL(request.url).pathname
|
||||
try {
|
||||
const response = await fetch(request)
|
||||
if (shellRoutes.includes(pathname)
|
||||
&& response.ok
|
||||
&& response.headers.get('content-type')?.includes('text/html')) {
|
||||
await cache.put(pathname, response.clone())
|
||||
}
|
||||
return response
|
||||
} catch {
|
||||
return (await cache.match(request, { ignoreSearch: true }))
|
||||
|| (await cache.match(shellPath))
|
||||
|| Response.error()
|
||||
}
|
||||
}
|
||||
|
||||
async function staticResponse(request) {
|
||||
const cached = await caches.match(request, { ignoreSearch: false })
|
||||
if (cached) return cached
|
||||
const response = await fetch(request)
|
||||
if (response.ok) {
|
||||
const cache = await caches.open(cacheName)
|
||||
await cache.put(request, response.clone())
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
self.addEventListener('fetch', event => {
|
||||
const request = event.request
|
||||
if (request.method !== 'GET') return
|
||||
const url = new URL(request.url)
|
||||
if (url.origin !== self.location.origin) return
|
||||
|
||||
// Authentication, component data, WebSockets and OBS are outside this
|
||||
// worker's scope and are never cached. Keep the path check as defence in depth.
|
||||
if (request.mode === 'navigate' && isControlShell(url.pathname)) {
|
||||
event.respondWith(navigationResponse(request))
|
||||
return
|
||||
}
|
||||
if (isStaticAsset(url.pathname)) event.respondWith(staticResponse(request))
|
||||
})
|
||||
Reference in New Issue
Block a user