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: {