diff --git a/package-lock.json b/package-lock.json
index 89c41721c7..6206025e08 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14857,6 +14857,28 @@
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
"integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8="
},
+ "workbox-core": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-5.1.3.tgz",
+ "integrity": "sha512-TFSIPxxciX9sFaj0FDiohBeIKpwMcCyNduydi9i3LChItcndDS6TJpErxybv8aBWeCMraXt33TWtF6kKuIObNw=="
+ },
+ "workbox-routing": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-5.1.3.tgz",
+ "integrity": "sha512-F+sAp9Iy3lVl3BEG+pzXWVq4AftzjiFpHDaZ4Kf4vLoBoKQE0hIHet4zE5DpHqYdyw+Udhp4wrfHamX6PN6z1Q==",
+ "requires": {
+ "workbox-core": "^5.1.3"
+ }
+ },
+ "workbox-strategies": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-5.1.3.tgz",
+ "integrity": "sha512-wiXHfmOKnWABeIVW+/ye0e00+2CcS5y7SIj2f9zcdy2ZLEbcOf7B+yOl5OrWpBGlTUwRjIYhV++ZqiKm3Dc+8w==",
+ "requires": {
+ "workbox-core": "^5.1.3",
+ "workbox-routing": "^5.1.3"
+ }
+ },
"worker-farm": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
diff --git a/package.json b/package.json
index 1e81ea1448..e818e7d7c3 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,8 @@
"webpack": "4.43.0",
"webpack-cli": "3.3.11",
"webpack-fix-style-only-entries": "0.4.0",
+ "workbox-routing": "5.1.3",
+ "workbox-strategies": "5.1.3",
"worker-loader": "2.0.0"
},
"devDependencies": {
diff --git a/routers/routes/routes.go b/routers/routes/routes.go
index 7f409eb576..d739f0b6ca 100644
--- a/routers/routes/routes.go
+++ b/routers/routes/routes.go
@@ -1048,10 +1048,6 @@ func RegisterRoutes(m *macaron.Macaron) {
ctx.HTML(200, "pwa/manifest_json")
})
- m.Get("/serviceworker.js", templates.JSRenderer(), func(ctx *context.Context) {
- ctx.HTML(200, "pwa/serviceworker_js")
- })
-
// prometheus metrics endpoint
if setting.Metrics.Enabled {
c := metrics.NewCollector()
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl
index 8e58c07d23..2b2791f829 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -6,30 +6,6 @@
{{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}} {{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}
- {{if UseServiceWorker}}
-
- {{else}}
-
- {{end}}
@@ -86,6 +62,7 @@
window.config = {
AppSubUrl: '{{AppSubUrl}}',
StaticUrlPrefix: '{{StaticUrlPrefix}}',
+ UseServiceWorker: {{UseServiceWorker}},
csrf: '{{.CsrfToken}}',
HighlightJS: {{if .RequireHighlightJS}}true{{else}}false{{end}},
Minicolors: {{if .RequireMinicolors}}true{{else}}false{{end}},
diff --git a/templates/pwa/serviceworker_js.tmpl b/templates/pwa/serviceworker_js.tmpl
deleted file mode 100644
index a074879f3e..0000000000
--- a/templates/pwa/serviceworker_js.tmpl
+++ /dev/null
@@ -1,83 +0,0 @@
-var STATIC_CACHE = 'static-cache-v1';
-var urlsToCache = [
- // js
- '{{StaticUrlPrefix}}/fomantic/semantic.min.js?v={{MD5 AppVer}}',
- '{{StaticUrlPrefix}}/js/clipboard.js',
- '{{StaticUrlPrefix}}/js/gitgraph.js',
- '{{StaticUrlPrefix}}/js/highlight.js',
- '{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}',
- '{{StaticUrlPrefix}}/js/jquery.js?v={{MD5 AppVer}}',
- '{{StaticUrlPrefix}}/js/swagger.js?v={{MD5 AppVer}}',
- '{{StaticUrlPrefix}}/js/dropzone.js',
- '{{StaticUrlPrefix}}/js/datetimepicker.js',
- '{{StaticUrlPrefix}}/js/tribute.js',
- '{{StaticUrlPrefix}}/vendor/plugins/codemirror/addon/mode/loadmode.js',
- '{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/meta.js',
- '{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.min.js',
- '{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.js',
-
- // css
- '{{StaticUrlPrefix}}/css/index.css?v={{MD5 AppVer}}',
- '{{StaticUrlPrefix}}/css/swagger.css?v={{MD5 AppVer}}',
- '{{StaticUrlPrefix}}/css/dropzone.css',
- '{{StaticUrlPrefix}}/css/datetimepicker.css',
- '{{StaticUrlPrefix}}/fomantic/semantic.min.css?v={{MD5 AppVer}}',
- '{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css',
- '{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css',
- '{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css',
-{{if .IsSigned }}
- {{ if ne .SignedUser.Theme "gitea" }}
- '{{StaticUrlPrefix}}/css/theme-{{.SignedUser.Theme}}.css?v={{MD5 AppVer}}',
- {{end}}
-{{else if ne DefaultTheme "gitea"}}
- '{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}',
-{{end}}
-
- // img
- '{{StaticUrlPrefix}}/img/gitea-sm.png',
- '{{StaticUrlPrefix}}/img/gitea-lg.png',
-
- // svg
- '{{StaticUrlPrefix}}/img/svg/icons.svg',
-
- // fonts
- '{{StaticUrlPrefix}}/fomantic/themes/default/assets/fonts/icons.woff2',
- '{{StaticUrlPrefix}}/vendor/assets/roboto-fonts/roboto-v20-latin-ext_cyrillic-ext_latin_greek_vietnamese_cyrillic_greek-ext-regular.woff2',
- '{{StaticUrlPrefix}}/vendor/assets/roboto-fonts/roboto-v20-latin-ext_cyrillic-ext_latin_greek_vietnamese_cyrillic_greek-ext-italic.woff2',
- '{{StaticUrlPrefix}}/vendor/assets/roboto-fonts/roboto-v20-latin-ext_cyrillic-ext_latin_greek_vietnamese_cyrillic_greek-ext-700.woff2',
- '{{StaticUrlPrefix}}/vendor/assets/roboto-fonts/roboto-v20-latin-ext_cyrillic-ext_latin_greek_vietnamese_cyrillic_greek-ext-700italic.woff2',
-
- // monaco
- '{{StaticUrlPrefix}}/css/monaco.css',
- '{{StaticUrlPrefix}}/fonts/codicon.ttf',
- '{{StaticUrlPrefix}}/js/monaco-css.worker.js',
- '{{StaticUrlPrefix}}/js/monaco-editor.worker.js',
- '{{StaticUrlPrefix}}/js/monaco-html.worker.js',
- '{{StaticUrlPrefix}}/js/monaco-json.worker.js',
- '{{StaticUrlPrefix}}/js/monaco.js',
- '{{StaticUrlPrefix}}/js/monaco-ts.worker.js'
-];
-
-self.addEventListener('install', function (event) {
- // Perform install steps
- event.waitUntil(
- caches.open(STATIC_CACHE)
- .then(function (cache) {
- return cache.addAll(urlsToCache);
- })
- );
-});
-
-self.addEventListener('fetch', function (event) {
- event.respondWith(
- caches.match(event.request)
- .then(function (response) {
- // Cache hit - return response
- if (response) {
- return response;
- }
- return fetch(event.request);
- }
- )
- );
-});
diff --git a/web_src/js/features/serviceworker.js b/web_src/js/features/serviceworker.js
new file mode 100644
index 0000000000..aa25870538
--- /dev/null
+++ b/web_src/js/features/serviceworker.js
@@ -0,0 +1,24 @@
+const {UseServiceWorker, AppSubUrl} = window.config;
+
+async function unregister() {
+ for (const registration of await navigator.serviceWorker.getRegistrations()) {
+ const serviceWorker = registration.active;
+ if (!serviceWorker) continue;
+ registration.unregister();
+ }
+}
+
+export default async function initServiceWorker() {
+ if (!('serviceWorker' in navigator)) return;
+
+ if (UseServiceWorker) {
+ try {
+ navigator.serviceWorker.register(`${AppSubUrl}/serviceworker.js`);
+ } catch (err) {
+ console.error(err);
+ await unregister();
+ }
+ } else {
+ await unregister();
+ }
+}
diff --git a/web_src/js/index.js b/web_src/js/index.js
index fdc5a926db..84e08c1dd3 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -14,6 +14,7 @@ import initGitGraph from './features/gitgraph.js';
import initClipboard from './features/clipboard.js';
import initUserHeatmap from './features/userheatmap.js';
import initDateTimePicker from './features/datetimepicker.js';
+import initServiceWorker from './features/serviceworker.js';
import attachTribute from './features/tribute.js';
import createDropzone from './features/dropzone.js';
import highlight from './features/highlight.js';
@@ -2475,6 +2476,7 @@ $(document).ready(async () => {
initGitGraph(),
initClipboard(),
initUserHeatmap(),
+ initServiceWorker(),
]);
});
diff --git a/web_src/js/serviceworker.js b/web_src/js/serviceworker.js
new file mode 100644
index 0000000000..1b00905582
--- /dev/null
+++ b/web_src/js/serviceworker.js
@@ -0,0 +1,14 @@
+import {registerRoute} from 'workbox-routing';
+import {StaleWhileRevalidate} from 'workbox-strategies';
+
+const cachedDestinations = new Set([
+ 'manifest',
+ 'script',
+ 'style',
+ 'worker',
+]);
+
+registerRoute(
+ ({request}) => cachedDestinations.has(request.destination),
+ new StaleWhileRevalidate({cacheName: 'static-cache-v2'}),
+);
diff --git a/webpack.config.js b/webpack.config.js
index d6a632ad1f..766fa76ae4 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -35,13 +35,20 @@ module.exports = {
jquery: [
resolve(__dirname, 'web_src/js/jquery.js'),
],
+ serviceworker: [
+ resolve(__dirname, 'web_src/js/serviceworker.js'),
+ ],
icons: glob('node_modules/@primer/octicons/build/svg/**/*.svg'),
...themes,
},
devtool: false,
output: {
path: resolve(__dirname, 'public'),
- filename: 'js/[name].js',
+ filename: ({chunk}) => {
+ // serviceworker can only manage assets below it's script path so we
+ // have to put it in / instead of /js/
+ return chunk.id === 'serviceworker' ? '[name].js' : 'js/[name].js';
+ },
chunkFilename: 'js/[name].js',
},
optimization: {