From bb28ecaff765ca2ac70358a6c88c4118a2c4fec5 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Thu, 7 Mar 2019 21:14:50 +0200 Subject: [PATCH 1/2] Use async/await in service worker, do not wait for cache open/put for successful requests --- client/service-worker.js | 45 +++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/client/service-worker.js b/client/service-worker.js index 18c67fb4..e05df454 100644 --- a/client/service-worker.js +++ b/client/service-worker.js @@ -2,6 +2,7 @@ /* global clients */ "use strict"; +const cacheName = "thelounge"; const excludedPathsFromCache = /^(?:socket\.io|storage|uploads|cdn-cgi)\//; self.addEventListener("install", function() { @@ -36,31 +37,33 @@ self.addEventListener("fetch", function(event) { uri.hash = ""; uri.search = ""; - event.respondWith(networkOrCache(uri)); + event.respondWith(networkOrCache(event, uri)); }); -function networkOrCache(uri) { - return caches.open("thelounge").then(function(cache) { - // Despite the "no-cache" name, it is a conditional request if proper headers are set - return fetch(uri, {cache: "no-cache"}) - .then(function(response) { - if (response.ok) { - return cache.put(uri, response.clone()).then(function() { - return response; - }); - } +async function putInCache(uri, response) { + const cache = await caches.open(cacheName); + await cache.put(uri, response); +} - // eslint-disable-next-line no-console - console.error(`Request for ${uri.href} failed with HTTP ${response.status}`); +async function networkOrCache(event, uri) { + try { + const response = await fetch(uri, {cache: "no-cache"}); - return Promise.reject("request-failed"); - }) - .catch(function() { - return cache.match(uri).then(function(matching) { - return matching || Promise.reject("request-not-in-cache"); - }); - }); - }); + if (response.ok) { + event.waitUntil(putInCache(uri, response)); + return response.clone(); + } + + throw new Error(`Request failed with HTTP ${response.status}`); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e.message, uri.href); + + const cache = await caches.open(cacheName); + const matching = await cache.match(uri); + + return matching || Promise.reject("request-not-in-cache"); + } } self.addEventListener("message", function(event) { From de9459dd83507cb80de643529c5595563cf3fe74 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Fri, 8 Mar 2019 12:29:49 +0200 Subject: [PATCH 2/2] Implement cache busting based on version hash --- client/index.html.tpl | 15 ++++++++------- client/service-worker.js | 28 +++++++++++++++------------- src/helper.js | 8 ++++++++ src/server.js | 5 ++++- webpack.config.js | 13 ++++++++++++- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/client/index.html.tpl b/client/index.html.tpl index 92db1ef8..4e4bc182 100644 --- a/client/index.html.tpl +++ b/client/index.html.tpl @@ -5,11 +5,12 @@ - - + + + - - + + <% _.forEach(stylesheets, function(css) { %> @@ -59,7 +60,7 @@

This is taking longer than it should, there might be connectivity issues.

- +
@@ -68,7 +69,7 @@
- - + + diff --git a/client/service-worker.js b/client/service-worker.js index e05df454..aa36f885 100644 --- a/client/service-worker.js +++ b/client/service-worker.js @@ -2,7 +2,7 @@ /* global clients */ "use strict"; -const cacheName = "thelounge"; +const cacheName = "__HASH__"; const excludedPathsFromCache = /^(?:socket\.io|storage|uploads|cdn-cgi)\//; self.addEventListener("install", function() { @@ -10,6 +10,12 @@ self.addEventListener("install", function() { }); self.addEventListener("activate", function(event) { + event.waitUntil(caches.keys().then((names) => Promise.all( + names + .filter((name) => name !== cacheName) + .map((name) => caches.delete(name)) + ))); + event.waitUntil(self.clients.claim()); }); @@ -33,34 +39,30 @@ self.addEventListener("fetch", function(event) { return; } - const uri = new URL(url); - uri.hash = ""; - uri.search = ""; - - event.respondWith(networkOrCache(event, uri)); + event.respondWith(networkOrCache(event)); }); -async function putInCache(uri, response) { +async function putInCache(request, response) { const cache = await caches.open(cacheName); - await cache.put(uri, response); + await cache.put(request, response); } -async function networkOrCache(event, uri) { +async function networkOrCache(event) { try { - const response = await fetch(uri, {cache: "no-cache"}); + const response = await fetch(event.request, {cache: "no-cache"}); if (response.ok) { - event.waitUntil(putInCache(uri, response)); + event.waitUntil(putInCache(event.request, response)); return response.clone(); } throw new Error(`Request failed with HTTP ${response.status}`); } catch (e) { // eslint-disable-next-line no-console - console.error(e.message, uri.href); + console.error(e.message, event.request.url); const cache = await caches.open(cacheName); - const matching = await cache.match(uri); + const matching = await cache.match(event.request); return matching || Promise.reject("request-not-in-cache"); } diff --git a/src/helper.js b/src/helper.js index d2f96f51..e34922f7 100644 --- a/src/helper.js +++ b/src/helper.js @@ -9,6 +9,7 @@ const fs = require("fs"); const net = require("net"); const bcrypt = require("bcryptjs"); const colors = require("chalk"); +const crypto = require("crypto"); let homePath; let configPath; @@ -32,6 +33,7 @@ const Helper = { getUserLogsPath, setHome, getVersion, + getVersionCacheBust, getGitCommit, ip2hex, mergeConfig, @@ -89,6 +91,12 @@ function getGitCommit() { } } +function getVersionCacheBust() { + const hash = crypto.createHash("sha256").update(Helper.getVersion()).digest("hex"); + + return hash.substring(0, 10); +} + function setHome(newPath) { homePath = expandHome(newPath); configPath = path.join(homePath, "config.js"); diff --git a/src/server.js b/src/server.js index ac18037f..c3f18cb4 100644 --- a/src/server.js +++ b/src/server.js @@ -281,7 +281,10 @@ function index(req, res, next) { throw err; } - res.send(_.template(file)(getServerConfiguration())); + const config = getServerConfiguration(); + config.cacheBust = Helper.getVersionCacheBust(); + + res.send(_.template(file)(config)); }); } diff --git a/webpack.config.js b/webpack.config.js index b168c280..08c0815a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,6 +5,7 @@ const path = require("path"); const CopyPlugin = require("copy-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const VueLoaderPlugin = require("vue-loader/lib/plugin"); +const Helper = require("./src/helper.js"); const config = { mode: process.env.NODE_ENV === "production" ? "production" : "development", @@ -123,7 +124,17 @@ const config = { { from: "./client/*", to: "[name].[ext]", - ignore: "index.html.tpl", + ignore: [ + "index.html.tpl", + "service-worker.js", + ], + }, + { + from: "./client/service-worker.js", + to: "[name].[ext]", + transform(content) { + return content.toString().replace("__HASH__", Helper.getVersionCacheBust()); + }, }, { from: "./client/audio/*",