Implement cache busting based on version hash

This commit is contained in:
Pavel Djundik 2019-03-08 12:29:49 +02:00
parent bb28ecaff7
commit de9459dd83
5 changed files with 47 additions and 22 deletions

View file

@ -5,11 +5,12 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no"> <meta name="viewport" content="width=device-width, user-scalable=no">
<link rel="preload" as="script" href="js/bundle.vendor.js"> <link rel="preload" as="script" href="js/loading-error-handlers.js?v=<%- cacheBust %>">
<link rel="preload" as="script" href="js/bundle.js"> <link rel="preload" as="script" href="js/bundle.vendor.js?v=<%- cacheBust %>">
<link rel="preload" as="script" href="js/bundle.js?v=<%- cacheBust %>">
<link rel="stylesheet" href="css/primer-tooltips.css"> <link rel="stylesheet" href="css/primer-tooltips.css?v=<%- cacheBust %>">
<link rel="stylesheet" href="css/style.css"> <link rel="stylesheet" href="css/style.css?v=<%- cacheBust %>">
<link id="theme" rel="stylesheet" href="themes/<%- theme %>.css" data-server-theme="<%- theme %>"> <link id="theme" rel="stylesheet" href="themes/<%- theme %>.css" data-server-theme="<%- theme %>">
<% _.forEach(stylesheets, function(css) { %> <% _.forEach(stylesheets, function(css) { %>
<link rel="stylesheet" href="packages/<%- css %>"> <link rel="stylesheet" href="packages/<%- css %>">
@ -59,7 +60,7 @@
<p id="loading-slow">This is taking longer than it should, there might be connectivity issues.</p> <p id="loading-slow">This is taking longer than it should, there might be connectivity issues.</p>
<button id="loading-reload" class="btn">Reload page</button> <button id="loading-reload" class="btn">Reload page</button>
</div> </div>
<script async src="js/loading-error-handlers.js"></script> <script async src="js/loading-error-handlers.js?v=<%- cacheBust %>"></script>
</div> </div>
</div> </div>
<div id="viewport"></div> <div id="viewport"></div>
@ -68,7 +69,7 @@
<div id="image-viewer"></div> <div id="image-viewer"></div>
<div id="upload-overlay"></div> <div id="upload-overlay"></div>
<script src="js/bundle.vendor.js"></script> <script src="js/bundle.vendor.js?v=<%- cacheBust %>"></script>
<script src="js/bundle.js"></script> <script src="js/bundle.js?v=<%- cacheBust %>"></script>
</body> </body>
</html> </html>

View file

@ -2,7 +2,7 @@
/* global clients */ /* global clients */
"use strict"; "use strict";
const cacheName = "thelounge"; const cacheName = "__HASH__";
const excludedPathsFromCache = /^(?:socket\.io|storage|uploads|cdn-cgi)\//; const excludedPathsFromCache = /^(?:socket\.io|storage|uploads|cdn-cgi)\//;
self.addEventListener("install", function() { self.addEventListener("install", function() {
@ -10,6 +10,12 @@ self.addEventListener("install", function() {
}); });
self.addEventListener("activate", function(event) { 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()); event.waitUntil(self.clients.claim());
}); });
@ -33,34 +39,30 @@ self.addEventListener("fetch", function(event) {
return; return;
} }
const uri = new URL(url); event.respondWith(networkOrCache(event));
uri.hash = "";
uri.search = "";
event.respondWith(networkOrCache(event, uri));
}); });
async function putInCache(uri, response) { async function putInCache(request, response) {
const cache = await caches.open(cacheName); 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 { try {
const response = await fetch(uri, {cache: "no-cache"}); const response = await fetch(event.request, {cache: "no-cache"});
if (response.ok) { if (response.ok) {
event.waitUntil(putInCache(uri, response)); event.waitUntil(putInCache(event.request, response));
return response.clone(); return response.clone();
} }
throw new Error(`Request failed with HTTP ${response.status}`); throw new Error(`Request failed with HTTP ${response.status}`);
} catch (e) { } catch (e) {
// eslint-disable-next-line no-console // 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 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"); return matching || Promise.reject("request-not-in-cache");
} }

View file

@ -9,6 +9,7 @@ const fs = require("fs");
const net = require("net"); const net = require("net");
const bcrypt = require("bcryptjs"); const bcrypt = require("bcryptjs");
const colors = require("chalk"); const colors = require("chalk");
const crypto = require("crypto");
let homePath; let homePath;
let configPath; let configPath;
@ -32,6 +33,7 @@ const Helper = {
getUserLogsPath, getUserLogsPath,
setHome, setHome,
getVersion, getVersion,
getVersionCacheBust,
getGitCommit, getGitCommit,
ip2hex, ip2hex,
mergeConfig, 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) { function setHome(newPath) {
homePath = expandHome(newPath); homePath = expandHome(newPath);
configPath = path.join(homePath, "config.js"); configPath = path.join(homePath, "config.js");

View file

@ -281,7 +281,10 @@ function index(req, res, next) {
throw err; throw err;
} }
res.send(_.template(file)(getServerConfiguration())); const config = getServerConfiguration();
config.cacheBust = Helper.getVersionCacheBust();
res.send(_.template(file)(config));
}); });
} }

View file

@ -5,6 +5,7 @@ const path = require("path");
const CopyPlugin = require("copy-webpack-plugin"); const CopyPlugin = require("copy-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin"); const VueLoaderPlugin = require("vue-loader/lib/plugin");
const Helper = require("./src/helper.js");
const config = { const config = {
mode: process.env.NODE_ENV === "production" ? "production" : "development", mode: process.env.NODE_ENV === "production" ? "production" : "development",
@ -123,7 +124,17 @@ const config = {
{ {
from: "./client/*", from: "./client/*",
to: "[name].[ext]", 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/*", from: "./client/audio/*",