fix: cache canonical GTIN page without redirects
This commit is contained in:
@@ -67,7 +67,9 @@ Werkzeug selbst verlinkt auf seine eigenständigen Rechtstexte unter `/gtin/impr
|
|||||||
- **GTIN-Generator.** Der Generator unter `/gtin/` rechnet vollständig im Browser,
|
- **GTIN-Generator.** Der Generator unter `/gtin/` rechnet vollständig im Browser,
|
||||||
ohne Konto, externe API oder Serverübertragung. Er speichert bis zu 20 zuletzt
|
ohne Konto, externe API oder Serverübertragung. Er speichert bis zu 20 zuletzt
|
||||||
erzeugte Einträge lokal im Browser; der Offline-Appcache enthält nur die
|
erzeugte Einträge lokal im Browser; der Offline-Appcache enthält nur die
|
||||||
Anwendungsdateien. Die eigenständige Datenschutzerklärung liegt unter
|
Anwendungsdateien. Der Service Worker speichert die kanonische Startadresse
|
||||||
|
`/gtin/` ohne Weiterleitung; jede Änderung am Werkzeug erhält eine neue
|
||||||
|
Cache-Version. Die eigenständige Datenschutzerklärung liegt unter
|
||||||
`/gtin/datenschutz/`; die Rechtstexte sind nicht mit denen des Hauptauftritts verknüpft.
|
`/gtin/datenschutz/`; die Rechtstexte sind nicht mit denen des Hauptauftritts verknüpft.
|
||||||
|
|
||||||
## Herkunft der Inhalte
|
## Herkunft der Inhalte
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import base64
|
import base64
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
|
import hashlib
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import shutil
|
import shutil
|
||||||
@@ -285,6 +286,7 @@ def build_html(table: dict[str, dict]) -> list[tuple[str, str]]:
|
|||||||
print(f" [html] {rel}")
|
print(f" [html] {rel}")
|
||||||
shutil.rmtree(SITE / "gtin", ignore_errors=True)
|
shutil.rmtree(SITE / "gtin", ignore_errors=True)
|
||||||
shutil.copytree(SRC / "gtin", SITE / "gtin")
|
shutil.copytree(SRC / "gtin", SITE / "gtin")
|
||||||
|
version_gtin_service_worker()
|
||||||
print(" [html] gtin/ (unlisted browser tool and standalone legal pages)")
|
print(" [html] gtin/ (unlisted browser tool and standalone legal pages)")
|
||||||
not_found = pages.not_found().replace("og-desfoto.jpg", "og-desfoto.jpg")
|
not_found = pages.not_found().replace("og-desfoto.jpg", "og-desfoto.jpg")
|
||||||
(SITE / "404.html").write_text(not_found, encoding="utf-8")
|
(SITE / "404.html").write_text(not_found, encoding="utf-8")
|
||||||
@@ -292,6 +294,26 @@ def build_html(table: dict[str, dict]) -> list[tuple[str, str]]:
|
|||||||
return written
|
return written
|
||||||
|
|
||||||
|
|
||||||
|
def version_gtin_service_worker() -> None:
|
||||||
|
"""Give each GTIN source release its own offline cache."""
|
||||||
|
digest = hashlib.sha256()
|
||||||
|
source_root = SRC / "gtin"
|
||||||
|
for path in sorted(source_root.rglob("*")):
|
||||||
|
if not path.is_file():
|
||||||
|
continue
|
||||||
|
digest.update(path.relative_to(source_root).as_posix().encode("utf-8"))
|
||||||
|
digest.update(b"\0")
|
||||||
|
digest.update(path.read_bytes())
|
||||||
|
digest.update(b"\0")
|
||||||
|
|
||||||
|
worker = SITE / "gtin" / "sw.js"
|
||||||
|
script = worker.read_text(encoding="utf-8")
|
||||||
|
marker = "__BUILD_VERSION__"
|
||||||
|
if script.count(marker) != 1:
|
||||||
|
raise RuntimeError("GTIN service worker cache version marker is missing or duplicated")
|
||||||
|
worker.write_text(script.replace(marker, digest.hexdigest()[:12]), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
def build_meta_files(routes: list[tuple[str, str]]) -> None:
|
def build_meta_files(routes: list[tuple[str, str]]) -> None:
|
||||||
today = dt.date.today().isoformat()
|
today = dt.date.today().isoformat()
|
||||||
urls = []
|
urls = []
|
||||||
|
|||||||
@@ -10,14 +10,13 @@
|
|||||||
|
|
||||||
const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {unknown} */ (self));
|
const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {unknown} */ (self));
|
||||||
|
|
||||||
const CACHE_VERSION = '15d23ab0263e';
|
const CACHE_VERSION = '403b5842d1cd';
|
||||||
const CACHE_PREFIX = 'desfoto-gtin-generator-';
|
const CACHE_PREFIX = 'desfoto-gtin-generator-';
|
||||||
const CACHE_NAME = `${CACHE_PREFIX}${CACHE_VERSION}`;
|
const CACHE_NAME = `${CACHE_PREFIX}${CACHE_VERSION}`;
|
||||||
|
|
||||||
// PRECACHE
|
// PRECACHE
|
||||||
const PRECACHE = [
|
const PRECACHE = [
|
||||||
'./',
|
'./',
|
||||||
'./index.html',
|
|
||||||
'./css/app.css',
|
'./css/app.css',
|
||||||
'./js/app.js',
|
'./js/app.js',
|
||||||
'./js/gtin.js',
|
'./js/gtin.js',
|
||||||
@@ -36,7 +35,7 @@ function scopeUrl(pfad) {
|
|||||||
return new URL(pfad, sw.registration.scope).toString();
|
return new URL(pfad, sw.registration.scope).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
const INDEX_URL = scopeUrl('./index.html');
|
const INDEX_URL = scopeUrl('./');
|
||||||
|
|
||||||
/** Pfad des Geltungsbereichs, z. B. "/gtin/". */
|
/** Pfad des Geltungsbereichs, z. B. "/gtin/". */
|
||||||
const SCOPE_PFAD = new URL(sw.registration.scope).pathname;
|
const SCOPE_PFAD = new URL(sw.registration.scope).pathname;
|
||||||
@@ -59,7 +58,7 @@ const SHELL_KENNUNG = 'id="artikelnummer"';
|
|||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
async function istAnwendungshuelle(antwort) {
|
async function istAnwendungshuelle(antwort) {
|
||||||
if (!antwort.ok || antwort.type !== 'basic') return false;
|
if (!antwort.ok || antwort.type !== 'basic' || antwort.redirected) return false;
|
||||||
const inhaltstyp = antwort.headers.get('content-type') ?? '';
|
const inhaltstyp = antwort.headers.get('content-type') ?? '';
|
||||||
if (!inhaltstyp.includes('text/html')) return false;
|
if (!inhaltstyp.includes('text/html')) return false;
|
||||||
const endgueltig = new URL(antwort.url);
|
const endgueltig = new URL(antwort.url);
|
||||||
@@ -147,8 +146,7 @@ sw.addEventListener('fetch', (ereignis) => {
|
|||||||
|
|
||||||
if (anfrage.mode === 'navigate') {
|
if (anfrage.mode === 'navigate') {
|
||||||
const appStart = new URL('./', sw.registration.scope).pathname;
|
const appStart = new URL('./', sw.registration.scope).pathname;
|
||||||
const appIndex = new URL('./index.html', sw.registration.scope).pathname;
|
if (ziel.pathname !== appStart) return;
|
||||||
if (ziel.pathname !== appStart && ziel.pathname !== appIndex) return;
|
|
||||||
ereignis.respondWith(bedieneNavigation(ereignis, anfrage));
|
ereignis.respondWith(bedieneNavigation(ereignis, anfrage));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -157,7 +155,7 @@ sw.addEventListener('fetch', (ereignis) => {
|
|||||||
caches.match(anfrage).then((treffer) => {
|
caches.match(anfrage).then((treffer) => {
|
||||||
if (treffer) return treffer;
|
if (treffer) return treffer;
|
||||||
return fetch(anfrage).then((antwort) => {
|
return fetch(anfrage).then((antwort) => {
|
||||||
if (antwort.ok && antwort.type === 'basic') {
|
if (antwort.ok && antwort.type === 'basic' && !antwort.redirected) {
|
||||||
const kopie = antwort.clone();
|
const kopie = antwort.clone();
|
||||||
ereignis.waitUntil(
|
ereignis.waitUntil(
|
||||||
caches
|
caches
|
||||||
|
|||||||
@@ -10,14 +10,13 @@
|
|||||||
|
|
||||||
const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {unknown} */ (self));
|
const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {unknown} */ (self));
|
||||||
|
|
||||||
const CACHE_VERSION = '15d23ab0263e';
|
const CACHE_VERSION = '__BUILD_VERSION__';
|
||||||
const CACHE_PREFIX = 'desfoto-gtin-generator-';
|
const CACHE_PREFIX = 'desfoto-gtin-generator-';
|
||||||
const CACHE_NAME = `${CACHE_PREFIX}${CACHE_VERSION}`;
|
const CACHE_NAME = `${CACHE_PREFIX}${CACHE_VERSION}`;
|
||||||
|
|
||||||
// PRECACHE
|
// PRECACHE
|
||||||
const PRECACHE = [
|
const PRECACHE = [
|
||||||
'./',
|
'./',
|
||||||
'./index.html',
|
|
||||||
'./css/app.css',
|
'./css/app.css',
|
||||||
'./js/app.js',
|
'./js/app.js',
|
||||||
'./js/gtin.js',
|
'./js/gtin.js',
|
||||||
@@ -36,7 +35,7 @@ function scopeUrl(pfad) {
|
|||||||
return new URL(pfad, sw.registration.scope).toString();
|
return new URL(pfad, sw.registration.scope).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
const INDEX_URL = scopeUrl('./index.html');
|
const INDEX_URL = scopeUrl('./');
|
||||||
|
|
||||||
/** Pfad des Geltungsbereichs, z. B. "/gtin/". */
|
/** Pfad des Geltungsbereichs, z. B. "/gtin/". */
|
||||||
const SCOPE_PFAD = new URL(sw.registration.scope).pathname;
|
const SCOPE_PFAD = new URL(sw.registration.scope).pathname;
|
||||||
@@ -59,7 +58,7 @@ const SHELL_KENNUNG = 'id="artikelnummer"';
|
|||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
async function istAnwendungshuelle(antwort) {
|
async function istAnwendungshuelle(antwort) {
|
||||||
if (!antwort.ok || antwort.type !== 'basic') return false;
|
if (!antwort.ok || antwort.type !== 'basic' || antwort.redirected) return false;
|
||||||
const inhaltstyp = antwort.headers.get('content-type') ?? '';
|
const inhaltstyp = antwort.headers.get('content-type') ?? '';
|
||||||
if (!inhaltstyp.includes('text/html')) return false;
|
if (!inhaltstyp.includes('text/html')) return false;
|
||||||
const endgueltig = new URL(antwort.url);
|
const endgueltig = new URL(antwort.url);
|
||||||
@@ -147,8 +146,7 @@ sw.addEventListener('fetch', (ereignis) => {
|
|||||||
|
|
||||||
if (anfrage.mode === 'navigate') {
|
if (anfrage.mode === 'navigate') {
|
||||||
const appStart = new URL('./', sw.registration.scope).pathname;
|
const appStart = new URL('./', sw.registration.scope).pathname;
|
||||||
const appIndex = new URL('./index.html', sw.registration.scope).pathname;
|
if (ziel.pathname !== appStart) return;
|
||||||
if (ziel.pathname !== appStart && ziel.pathname !== appIndex) return;
|
|
||||||
ereignis.respondWith(bedieneNavigation(ereignis, anfrage));
|
ereignis.respondWith(bedieneNavigation(ereignis, anfrage));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -157,7 +155,7 @@ sw.addEventListener('fetch', (ereignis) => {
|
|||||||
caches.match(anfrage).then((treffer) => {
|
caches.match(anfrage).then((treffer) => {
|
||||||
if (treffer) return treffer;
|
if (treffer) return treffer;
|
||||||
return fetch(anfrage).then((antwort) => {
|
return fetch(anfrage).then((antwort) => {
|
||||||
if (antwort.ok && antwort.type === 'basic') {
|
if (antwort.ok && antwort.type === 'basic' && !antwort.redirected) {
|
||||||
const kopie = antwort.clone();
|
const kopie = antwort.clone();
|
||||||
ereignis.waitUntil(
|
ereignis.waitUntil(
|
||||||
caches
|
caches
|
||||||
|
|||||||
Reference in New Issue
Block a user