AzuraCast/src/Controller/Frontend/PWA/ServiceWorkerAction.php

119 lines
3.5 KiB
PHP

<?php
namespace App\Controller\Frontend\PWA;
use App\Assets;
use App\Environment;
use App\Http\Response;
use App\Http\ServerRequest;
use DI\FactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\Finder\Finder;
class ServiceWorkerAction
{
public const SW_BASE = <<<'JS'
const cacheName = 'assets'; // Change value to force update
const cacheFiles = [];
self.addEventListener('install', event => {
// Kick out the old service worker
self.skipWaiting();
event.waitUntil(
caches.open(cacheName).then(cache => {
return cache.addAll(cacheFiles);
})
);
});
self.addEventListener('activate', event => {
// Delete any non-current cache
event.waitUntil(
caches.keys().then(keys => {
Promise.all(
keys.map(key => {
if (![cacheName].includes(key)) {
return caches.delete(key);
}
})
);
})
);
});
// Offline-first, cache-first strategy
// Kick off two asynchronous requests, one to the cache and one to the network
// If there's a cached version available, use it, but fetch an update for next time.
// Gets data on screen as quickly as possible, then updates once the network has returned the latest data.
self.addEventListener('fetch', event => {
event.respondWith(
caches.open(cacheName).then(cache => {
return cache.match(event.request).then(response => {
return response || fetch(event.request).then(networkResponse => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
});
})
);
});
JS;
public function __invoke(
ServerRequest $request,
Response $response,
Environment $environment,
FactoryInterface $factory
): ResponseInterface {
$assets = $factory->make(
Assets::class,
[
'request' => $request,
]
);
$swContents = self::SW_BASE;
$findString = 'const cacheFiles = [];';
if (!str_contains($swContents, $findString)) {
throw new \RuntimeException('Service worker template does not contain proper placeholder.');
}
$cacheFiles = [];
// Cache the compiled assets that would be used on public players.
$assets->load('minimal')
->load('Vue_PublicFullPlayer');
$loadedFiles = $assets->getLoadedFiles();
foreach ($loadedFiles as $file) {
if (!str_starts_with($file, 'http')) {
$cacheFiles[] = $file;
}
}
// Cache images and icons
$staticBase = $environment->getBaseDirectory() . '/web' . $environment->getAssetUrl();
$otherStaticFiles = Finder::create()
->files()
->in($staticBase)
->depth('>=1')
->exclude(['dist', 'api']);
foreach($otherStaticFiles as $file) {
$cacheFiles[] = $environment->getAssetUrl().'/'.$file->getRelativePathname();
}
$replaceString = 'const cacheFiles = ' . json_encode($cacheFiles) . ';';
$swContents = str_replace($findString, $replaceString, $swContents);
return $response
->withHeader('Content-Type', 'text/javascript')
->write($swContents);
}
}