wip service worker

This commit is contained in:
vabene1111
2025-06-19 17:48:06 +02:00
parent 7dc9e012fa
commit 2a6a785453
6 changed files with 2761 additions and 13 deletions

View File

@@ -12,6 +12,7 @@
<html lang="{{ LANGUAGE_CODE }}"> <html lang="{{ LANGUAGE_CODE }}">
<head> <head>
<title>Tandoor</title> <title>Tandoor</title>
<meta name="description" content="{% trans 'Tandoor Recipe Manager' %}">
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, minimal-ui, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, minimal-ui, shrink-to-fit=no">
<meta name="robots" content="noindex,nofollow"/> <meta name="robots" content="noindex,nofollow"/>
@@ -43,16 +44,16 @@
<script type="application/javascript"> <script type="application/javascript">
localStorage.setItem('BASE_PATH', "{% base_path request 'base' %}") localStorage.setItem('BASE_PATH', "{% base_path request 'base' %}")
window.addEventListener("load", () => { {#window.addEventListener("load", () => {#}
if ("serviceWorker" in navigator) { {# if ("serviceWorker" in navigator) {#}
navigator.serviceWorker.register("{% url 'service_worker' %}", {scope: "{% base_path request 'base' %}" + '/'}).then(function (reg) { {# navigator.serviceWorker.register("{% url 'service_worker' %}", {scope: "{% base_path request 'base' %}" + '/'}).then(function (reg) {#}
}).catch(function (err) { {# }).catch(function (err) {#}
console.warn('Error whilst registering service worker', err); {# console.warn('Error whilst registering service worker', err);#}
}); {# });#}
} else { {# } else {#}
console.warn('service worker not in navigator'); {# console.warn('service worker not in navigator');#}
} {# }#}
}); {#});#}
</script> </script>
</body> </body>

View File

@@ -523,6 +523,9 @@ def report_share_abuse(request, token):
return HttpResponseRedirect(reverse('index')) return HttpResponseRedirect(reverse('index'))
def service_worker(request):
return
def web_manifest(request): def web_manifest(request):
theme_values = get_theming_values(request) theme_values = get_theming_values(request)

View File

@@ -34,6 +34,7 @@
"jsdom": "^26.1.0", "jsdom": "^26.1.0",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vite": "6.2.6", "vite": "6.2.6",
"vite-plugin-pwa": "^1.0.0",
"vite-plugin-vuetify": "^2.1.1", "vite-plugin-vuetify": "^2.1.1",
"vue-tsc": "^2.2.8" "vue-tsc": "^2.2.8"
} }

137
vue3/src/sw.ts Normal file
View File

@@ -0,0 +1,137 @@
// These JavaScript module imports need to be bundled:
import {precacheAndRoute, cleanupOutdatedCaches} from 'workbox-precaching';
import {registerRoute, setCatchHandler} from 'workbox-routing';
import {CacheFirst, NetworkFirst, NetworkOnly, StaleWhileRevalidate} from 'workbox-strategies';
import {ExpirationPlugin} from 'workbox-expiration';
import {BackgroundSyncPlugin, Queue} from "workbox-background-sync";
cleanupOutdatedCaches()
declare let self: ServiceWorkerGlobalScope
precacheAndRoute(self.__WB_MANIFEST)
const OFFLINE_CACHE_NAME = 'offline-html';
let script_name = typeof window !== 'undefined' ? localStorage.getItem('SCRIPT_NAME') : '/'
var OFFLINE_PAGE_URL = script_name + 'offline/';
self.addEventListener('install', async (event) => {
event.waitUntil(
caches.open(OFFLINE_CACHE_NAME).then((cache) => cache.add(new Request(OFFLINE_PAGE_URL, {cache: "reload"})))
);
});
// default handler if everything else fails
setCatchHandler(({event}) => {
switch (event.request.destination) {
case 'document':
console.log('Triggered fallback HTML')
return caches.open(OFFLINE_CACHE_NAME).then((cache) => cache.match(OFFLINE_PAGE_URL))
default:
console.log('Triggered response ERROR')
return Response.error();
}
});
registerRoute(
({request}) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [
new ExpirationPlugin({
maxEntries: 20,
}),
],
}),
);
registerRoute(
({request}) => (request.destination === 'script' || request.destination === 'style'),
new NetworkFirst({
cacheName: 'assets'
})
)
registerRoute(
new RegExp('jsreverse'),
new StaleWhileRevalidate({
cacheName: 'assets'
})
)
registerRoute(
new RegExp('jsi18n'),
new StaleWhileRevalidate({
cacheName: 'assets'
})
)
registerRoute(
new RegExp('api/recipe/([0-9]+)'),
new NetworkFirst({
cacheName: 'api-recipe',
plugins: [
new ExpirationPlugin({
maxEntries: 50,
}),
],
})
)
const queue = new Queue('shopping-sync-queue', {
maxRetentionTime: 7 * 24 * 60,
});
registerRoute(
new RegExp('api/shopping-list-entry/([0-9]+)'),
new NetworkOnly({
plugins: [
{
fetchDidFail: async ({request}) => {
await queue.pushRequest({request});
},
}
],
}),
'PATCH'
)
addEventListener('message', (event) => {
if (event.data.type === 'BGSYNC_REPLAY_REQUESTS') {
queue.replayRequests().then((r) => {
event.ports[0].postMessage('REPLAY_SUCCESS SW');
}).catch((err) => {
event.ports[0].postMessage('REPLAY_FAILURE');
});
}
if (event.data.type === 'BGSYNC_COUNT_QUEUE') {
queue.getAll().then((r) => {
event.ports[0].postMessage(r.length);
})
}
});
registerRoute(
new RegExp('api/*'),
new NetworkFirst({
cacheName: 'api',
plugins: [
new ExpirationPlugin({
maxEntries: 50
}),
],
})
)
registerRoute(
({request}) => request.destination === 'document',
new NetworkFirst({
cacheName: 'html',
plugins: [
new ExpirationPlugin({
maxAgeSeconds: 60 * 60 * 24 * 30,
maxEntries: 50,
}),
],
})
)

View File

@@ -3,6 +3,7 @@ import {fileURLToPath, URL} from 'node:url'
import {defineConfig} from 'vite' import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import vuetify, {transformAssetUrls} from 'vite-plugin-vuetify' import vuetify, {transformAssetUrls} from 'vite-plugin-vuetify'
import {VitePWA} from "vite-plugin-pwa";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
@@ -14,6 +15,16 @@ export default defineConfig({
vuetify({ vuetify({
autoImport: true, autoImport: true,
}), }),
VitePWA({
registerType: 'autoUpdate',
strategies: 'injectManifest',
srcDir: 'src',
filename: 'sw.ts',
injectManifest: {
swDest: "../cookbook/templates/sw.js",
injectionPoint: undefined
}
})
], ],
resolve: { resolve: {
alias: { alias: {

File diff suppressed because it is too large Load Diff