Skip to content
Snippets Groups Projects
Commit 1bdbce03 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Merge branch 'service-worker-cache-api' into 'develop'

Cache API/media files with service workers

See merge request funkwhale/funkwhale!991
parents cf555b8a bd2c5189
No related branches found
No related tags found
No related merge requests found
...@@ -263,7 +263,7 @@ export default { ...@@ -263,7 +263,7 @@ export default {
updateApp () { updateApp () {
this.$store.commit('ui/serviceWorker', {updateAvailable: false}) this.$store.commit('ui/serviceWorker', {updateAvailable: false})
if (!this.serviceWorker.registration || !this.serviceWorker.registration.waiting) { return; } if (!this.serviceWorker.registration || !this.serviceWorker.registration.waiting) { return; }
this.serviceWorker.registration.waiting.postMessage('skipWaiting'); this.serviceWorker.registration.waiting.postMessage({command: 'skipWaiting'})
} }
}, },
computed: { computed: {
...@@ -317,9 +317,16 @@ export default { ...@@ -317,9 +317,16 @@ export default {
}, },
}, },
watch: { watch: {
'$store.state.instance.instanceUrl' () { '$store.state.instance.instanceUrl' (v) {
this.$store.dispatch('instance/fetchSettings') this.$store.dispatch('instance/fetchSettings')
this.fetchNodeInfo() this.fetchNodeInfo()
if (this.serviceWorker.registration) {
let sw = this.serviceWorker.registration.active
if (sw) {
sw.postMessage({command: 'serverChosen', serverUrl: v})
}
}
}, },
'$store.state.ui.theme': { '$store.state.ui.theme': {
immediate: true, immediate: true,
......
...@@ -6,13 +6,10 @@ import store from './store' ...@@ -6,13 +6,10 @@ import store from './store'
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, { register(`${process.env.BASE_URL}service-worker.js`, {
ready () { ready (registration) {
console.log( console.log(
'App is being served from cache by a service worker.' 'App is being served from cache by a service worker.', registration
) )
},
registered (registration) {
console.log('Service worker has been registered.')
// check for updates every 2 hours // check for updates every 2 hours
var checkInterval = 1000 * 60 * 60 * 2 var checkInterval = 1000 * 60 * 60 * 2
// var checkInterval = 1000 * 5 // var checkInterval = 1000 * 5
...@@ -20,6 +17,13 @@ if (process.env.NODE_ENV === 'production') { ...@@ -20,6 +17,13 @@ if (process.env.NODE_ENV === 'production') {
console.log('Checking for service worker update…') console.log('Checking for service worker update…')
registration.update(); registration.update();
}, checkInterval); }, checkInterval);
store.commit('ui/serviceWorker', {registration: registration})
if (registration.active) {
registration.active.postMessage({command: 'serverChosen', serverUrl: store.state.instance.instanceUrl})
}
},
registered () {
console.log('Service worker has been registered.')
}, },
cached () { cached () {
console.log('Content has been cached for offline use.') console.log('Content has been cached for offline use.')
......
// This is the code piece that GenerateSW mode can't provide for us. // This is the code piece that GenerateSW mode can't provide for us.
// This code listens for the user's confirmation to update the app. // This code listens for the user's confirmation to update the app.
workbox.loadModule('workbox-routing');
workbox.loadModule('workbox-strategies');
workbox.loadModule('workbox-expiration');
self.addEventListener('message', (e) => { self.addEventListener('message', (e) => {
if (!e.data) { if (!e.data) {
return; return;
} }
console.log('[sw] received message', e.data)
switch (e.data) { switch (e.data.command) {
case 'skipWaiting': case 'skipWaiting':
self.skipWaiting(); self.skipWaiting();
break; break;
case 'serverChosen':
self.registerServerRoutes(e.data.serverUrl)
default: default:
// NOOP // NOOP
break; break;
...@@ -18,7 +24,7 @@ workbox.core.clientsClaim(); ...@@ -18,7 +24,7 @@ workbox.core.clientsClaim();
// The precaching code provided by Workbox. // The precaching code provided by Workbox.
self.__precacheManifest = [].concat(self.__precacheManifest || []); self.__precacheManifest = [].concat(self.__precacheManifest || []);
console.log('Files to be cached by service worker [before filtering]', self.__precacheManifest.length); console.log('[sw] Files to be cached [before filtering]', self.__precacheManifest.length);
var excludedUrlsPrefix = [ var excludedUrlsPrefix = [
'/js/locale-', '/js/locale-',
'/js/moment-locale-', '/js/moment-locale-',
...@@ -28,6 +34,67 @@ var excludedUrlsPrefix = [ ...@@ -28,6 +34,67 @@ var excludedUrlsPrefix = [
self.__precacheManifest = self.__precacheManifest.filter((e) => { self.__precacheManifest = self.__precacheManifest.filter((e) => {
return !excludedUrlsPrefix.some(prefix => e.url.startsWith(prefix)) return !excludedUrlsPrefix.some(prefix => e.url.startsWith(prefix))
}); });
console.log('Files to be cached by service worker [after filtering]', self.__precacheManifest.length); console.log('[sw] Files to be cached [after filtering]', self.__precacheManifest.length);
// workbox.precaching.suppressWarnings(); // Only used with Vue CLI 3 and Workbox v3. // workbox.precaching.suppressWarnings(); // Only used with Vue CLI 3 and Workbox v3.
workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
const router = new workbox.routing.Router();
router.addCacheListener()
router.addFetchListener()
var registeredServerRoutes = []
self.registerServerRoutes = (serverUrl) => {
console.log('[sw] Setting up API caching for', serverUrl)
registeredServerRoutes.forEach((r) => {
console.log('[sw] Unregistering previous API route...', r)
router.unregisterRoute(r)
})
if (!serverUrl) {
return
}
var regexReadyServerUrl = serverUrl.replace('.', '\\.')
registeredServerRoutes = []
var networkFirstPaths = [
'api/v1/',
'media/',
]
var networkFirstExcludedPaths = [
'api/v1/listen'
]
var strategy = new workbox.strategies.NetworkFirst({
cacheName: "api-cache:" + serverUrl,
plugins: [
new workbox.expiration.Plugin({
maxAgeSeconds: 24 * 60 * 60 * 7,
}),
]
})
var networkFirstRoutes = networkFirstPaths.map((path) => {
var regex = new RegExp(regexReadyServerUrl + path)
return new workbox.routing.RegExpRoute(regex, () => {})
})
var matcher = ({url, event}) => {
for (let index = 0; index < networkFirstExcludedPaths.length; index++) {
const blacklistedPath = networkFirstExcludedPaths[index];
if (url.pathname.startsWith('/' + blacklistedPath)) {
// the path is blacklisted, we don't cache it at all
console.log('[sw] Path is blacklisted, not caching', url.pathname)
return false
}
}
// we call other regex matchers
for (let index = 0; index < networkFirstRoutes.length; index++) {
const route = networkFirstRoutes[index];
let result = route.match({url, event})
if (result) {
return result
}
}
return false
}
var route = new workbox.routing.Route(matcher, strategy)
console.log('[sw] registering new API route...', route)
router.registerRoute(route)
registeredServerRoutes.push(route)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment