pwa: add generic service worker (#24405)

This commit is contained in:
Frédéric Péters 2018-06-10 13:30:22 +02:00
parent b09080980d
commit dd7c193878
4 changed files with 132 additions and 2 deletions

View File

@ -0,0 +1,117 @@
{% load gadjo %}
/* global self, caches, fetch, URL, Response */
'use strict';
var config = {
version: 'v{% start_timestamp %}',
staticCacheItems: [
'/offline/'
],
cachePathPattern: /^\/static\/.*/,
handleFetchPathPattern: /.*/,
offlinePage: '/offline/'
};
function cacheName (key, opts) {
return `${opts.version}-${key}`;
}
function addToCache (cacheKey, request, response) {
if (response.ok && cacheKey !== null) {
var copy = response.clone();
caches.open(cacheKey).then( cache => {
cache.put(request, copy);
});
}
return response;
}
function fetchFromCache (event) {
return caches.match(event.request).then(response => {
if (!response) {
throw Error(`${event.request.url} not found in cache`);
}
return response;
});
}
function offlineResponse (resourceType, opts) {
if (resourceType === 'content') {
return caches.match(opts.offlinePage);
}
return undefined;
}
self.addEventListener('install', event => {
function onInstall (event, opts) {
var cacheKey = cacheName('static', opts);
return caches.open(cacheKey)
.then(cache => cache.addAll(opts.staticCacheItems));
}
event.waitUntil(
onInstall(event, config).then( () => self.skipWaiting() )
);
});
self.addEventListener('activate', event => {
function onActivate (event, opts) {
return caches.keys()
.then(cacheKeys => {
var oldCacheKeys = cacheKeys.filter(key => key.indexOf(opts.version) !== 0);
var deletePromises = oldCacheKeys.map(oldKey => caches.delete(oldKey));
return Promise.all(deletePromises);
});
}
event.waitUntil(
onActivate(event, config)
.then( () => self.clients.claim() )
);
});
self.addEventListener('fetch', event => {
function shouldHandleFetch (event, opts) {
var request = event.request;
var url = new URL(request.url);
var criteria = {
matchesPathPattern: opts.handleFetchPathPattern.test(url.pathname),
isGETRequest : request.method === 'GET',
isFromMyOrigin : url.origin === self.location.origin
};
var failingCriteria = Object.keys(criteria)
.filter(criteriaKey => !criteria[criteriaKey]);
return !failingCriteria.length;
}
function onFetch (event, opts) {
var request = event.request;
var url = new URL(request.url);
var acceptHeader = request.headers.get('Accept');
var resourceType = 'static';
var cacheKey;
if (acceptHeader.indexOf('text/html') !== -1) {
resourceType = 'content';
} else if (acceptHeader.indexOf('image') !== -1) {
resourceType = 'image';
}
cacheKey = null;
if (opts.cachePathPattern.test(url.pathname)) {
cacheKey = cacheName(resourceType, opts);
}
/* always network first */
event.respondWith(
fetch(request)
.then(response => addToCache(cacheKey, request, response))
.catch(() => fetchFromCache(event))
.catch(() => offlineResponse(resourceType, opts))
);
}
if (shouldHandleFetch(event, config)) {
onFetch(event, config);
}
});

View File

@ -16,6 +16,9 @@
from django.conf.urls import url
from .views import manifest_json
from .views import manifest_json, service_worker_js
urlpatterns = [url('^manifest.json', manifest_json)]
urlpatterns = [
url('^manifest.json', manifest_json),
url('^service-worker.js', service_worker_js),
]

View File

@ -17,9 +17,16 @@
from django.http import HttpResponse, Http404
from django.template.loader import get_template, TemplateDoesNotExist
def manifest_json(request, *args, **kwargs):
try:
template = get_template('combo/manifest.json')
except TemplateDoesNotExist:
raise Http404()
return HttpResponse(template.render({}, request), content_type='application/json')
def service_worker_js(request, *args, **kwargs):
template = get_template('combo/service-worker.js')
return HttpResponse(template.render({}, request),
content_type='application/javascript; charset=utf-8')

View File

@ -13,3 +13,6 @@ def test_manifest_json(app):
templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))]
with override_settings(TEMPLATES=templates_settings):
assert app.get('/manifest.json', status=200).json['name'] == 'test'
def test_service_worker(app):
app.get('/service-worker.js', status=200)