aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.eslintrc1
-rw-r--r--modules/templates/dynamic.go12
-rw-r--r--modules/templates/static.go9
-rw-r--r--package-lock.json22
-rw-r--r--package.json2
-rw-r--r--routers/routes/routes.go4
-rw-r--r--templates/base/head.tmpl26
-rw-r--r--templates/pwa/serviceworker_js.tmpl83
-rw-r--r--web_src/js/features/serviceworker.js43
-rw-r--r--web_src/js/index.js2
-rw-r--r--web_src/js/serviceworker.js16
-rw-r--r--webpack.config.js9
12 files changed, 96 insertions, 133 deletions
diff --git a/.eslintrc b/.eslintrc
index 8f337baec5..3a731cbf6b 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -48,6 +48,7 @@ rules:
no-cond-assign: [2, except-parens]
no-console: [1, {allow: [info, warn, error]}]
no-continue: [0]
+ no-empty: [2, {allowEmptyCatch: true}]
no-eq-null: [2]
no-mixed-operators: [0]
no-multi-assign: [0]
diff --git a/modules/templates/dynamic.go b/modules/templates/dynamic.go
index 6153e8d027..bd1c4d06c5 100644
--- a/modules/templates/dynamic.go
+++ b/modules/templates/dynamic.go
@@ -48,18 +48,6 @@ func JSONRenderer() macaron.Handler {
})
}
-// JSRenderer implements the macaron handler for serving JS templates.
-func JSRenderer() macaron.Handler {
- return macaron.Renderer(macaron.RenderOptions{
- Funcs: NewFuncMap(),
- Directory: path.Join(setting.StaticRootPath, "templates"),
- AppendDirectories: []string{
- path.Join(setting.CustomPath, "templates"),
- },
- HTMLContentType: "application/javascript",
- })
-}
-
// Mailer provides the templates required for sending notification mails.
func Mailer() (*texttmpl.Template, *template.Template) {
for _, funcs := range NewTextFuncMap() {
diff --git a/modules/templates/static.go b/modules/templates/static.go
index 5bc4e33e1c..a3aff5e567 100644
--- a/modules/templates/static.go
+++ b/modules/templates/static.go
@@ -132,15 +132,6 @@ func JSONRenderer() macaron.Handler {
})
}
-// JSRenderer implements the macaron handler for serving JS templates.
-func JSRenderer() macaron.Handler {
- return macaron.Renderer(macaron.RenderOptions{
- Funcs: NewFuncMap(),
- TemplateFileSystem: NewTemplateFileSystem(),
- HTMLContentType: "application/javascript",
- })
-}
-
// Mailer provides the templates required for sending notification mails.
func Mailer() (*texttmpl.Template, *template.Template) {
for _, funcs := range NewTextFuncMap() {
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..a6a715531d 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -6,30 +6,6 @@
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>{{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}} {{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}} </title>
<link rel="manifest" href="{{AppSubUrl}}/manifest.json" crossorigin="use-credentials">
- {{if UseServiceWorker}}
- <script>
- if ('serviceWorker' in navigator) {
- navigator.serviceWorker.register('{{AppSubUrl}}/serviceworker.js').then(function(registration) {
- // Registration was successful
- console.info('ServiceWorker registration successful with scope: ', registration.scope);
- }, function(err) {
- // registration failed :(
- console.info('ServiceWorker registration failed: ', err);
- });
- }
- </script>
- {{else}}
- <script>
- if ('serviceWorker' in navigator) {
- navigator.serviceWorker.getRegistrations().then(function(registrations) {
- registrations.forEach(function(registration) {
- registration.unregister();
- console.info('ServiceWorker unregistered');
- });
- });
- }
- </script>
- {{end}}
<meta name="theme-color" content="{{ThemeColorMetaTag}}">
<meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}" />
<meta name="description" content="{{if .Repository}}{{.Repository.Name}}{{if .Repository.Description}} - {{.Repository.Description}}{{end}}{{else}}{{MetaDescription}}{{end}}" />
@@ -84,8 +60,10 @@
</script>
<script>
window.config = {
+ AppVer: '{{AppVer}}',
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..a8fd2d41df
--- /dev/null
+++ b/web_src/js/features/serviceworker.js
@@ -0,0 +1,43 @@
+const {UseServiceWorker, AppSubUrl, AppVer} = window.config;
+const cacheName = 'static-cache-v2';
+
+async function unregister() {
+ for (const registration of await navigator.serviceWorker.getRegistrations()) {
+ const serviceWorker = registration.active;
+ if (!serviceWorker) continue;
+ registration.unregister();
+ }
+}
+
+async function invalidateCache() {
+ await caches.delete(cacheName);
+}
+
+async function checkCacheValidity() {
+ const cacheKey = AppVer;
+ const storedCacheKey = localStorage.getItem('staticCacheKey');
+
+ // invalidate cache if it belongs to a different gitea version
+ if (cacheKey && storedCacheKey !== cacheKey) {
+ invalidateCache();
+ localStorage.setItem('staticCacheKey', cacheKey);
+ }
+}
+
+export default async function initServiceWorker() {
+ if (!('serviceWorker' in navigator)) return;
+
+ if (UseServiceWorker) {
+ await checkCacheValidity();
+ try {
+ await navigator.serviceWorker.register(`${AppSubUrl}/serviceworker.js`);
+ } catch (err) {
+ console.error(err);
+ await invalidateCache();
+ await unregister();
+ }
+ } else {
+ await invalidateCache();
+ 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..e9dfde22f9
--- /dev/null
+++ b/web_src/js/serviceworker.js
@@ -0,0 +1,16 @@
+import {registerRoute} from 'workbox-routing';
+import {StaleWhileRevalidate} from 'workbox-strategies';
+
+const cacheName = 'static-cache-v2';
+
+const cachedDestinations = new Set([
+ 'manifest',
+ 'script',
+ 'style',
+ 'worker',
+]);
+
+registerRoute(
+ ({request}) => cachedDestinations.has(request.destination),
+ new StaleWhileRevalidate({cacheName}),
+);
diff --git a/webpack.config.js b/webpack.config.js
index d6a632ad1f..b1038c407d 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's directory so
+ // we have to put it in / instead of /js/
+ return chunk.id === 'serviceworker' ? '[name].js' : 'js/[name].js';
+ },
chunkFilename: 'js/[name].js',
},
optimization: {