From 35d4465d47945593a8fdb10d9a2dc0873385b5ce Mon Sep 17 00:00:00 2001 From: imperosol Date: Mon, 11 May 2026 13:16:37 +0200 Subject: [PATCH] feat: automatic localstorage cleaning --- core/static/bundled/base-bundle-index.ts | 6 ++ core/static/bundled/core/cache.ts | 24 ++++++++ docs/tutorial/front/localstorage.md | 56 +++++++++++++++++++ .../static/bundled/eboutic/eboutic-index.ts | 3 +- mkdocs.yml | 2 + 5 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 core/static/bundled/core/cache.ts create mode 100644 docs/tutorial/front/localstorage.md diff --git a/core/static/bundled/base-bundle-index.ts b/core/static/bundled/base-bundle-index.ts index 633fcd24..fec5da64 100644 --- a/core/static/bundled/base-bundle-index.ts +++ b/core/static/bundled/base-bundle-index.ts @@ -3,6 +3,7 @@ import Alpine from "alpinejs"; import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill"; import htmx from "htmx.org"; import { limitedChoices } from "#core:alpine/limited-choices"; +import { cacheBuster } from "#core:core/cache"; import { default as navbar } from "#core:core/navbar"; import { alpinePlugin as notificationPlugin } from "#core:utils/notifications"; @@ -41,3 +42,8 @@ Object.assign(window, { htmx }); * navbar */ navbar(); + +/** + * Script that clears the cache when the cache version changes + */ +cacheBuster(); diff --git a/core/static/bundled/core/cache.ts b/core/static/bundled/core/cache.ts new file mode 100644 index 00000000..5d44c986 --- /dev/null +++ b/core/static/bundled/core/cache.ts @@ -0,0 +1,24 @@ +// increment this number when a breaking change is made with localStorage +const CURRENT_CACHE_VERSION = 1; + +export function cacheBuster() { + const version = Number.parseInt(localStorage.getItem("version") ?? "0", 10); + if (version === CURRENT_CACHE_VERSION) { + // The cache schema is up-to-date. Nothing to do. + return; + } + localStorage.removeItem("basket"); + localStorage.removeItem("basket1"); + // remove all storage items which key is in the form + // `userXXXPictures` or `userXXXPicturesNumber` + Object.keys(localStorage) + .filter( + (key) => + key.startsWith("user") && + (key.endsWith("Pictures") || key.endsWith("PicturesNumber")), + ) + .forEach((key) => { + localStorage.removeItem(key); + }); + localStorage.setItem("version", CURRENT_CACHE_VERSION.toString()); +} diff --git a/docs/tutorial/front/localstorage.md b/docs/tutorial/front/localstorage.md new file mode 100644 index 00000000..bdcaf17c --- /dev/null +++ b/docs/tutorial/front/localstorage.md @@ -0,0 +1,56 @@ +[Documentation du localStorage (mozilla)](https://developer.mozilla.org/fr/docs/Web/API/Window/localStorage) + +## Utilité et limitations + +Le `localStorage` est un cache géré directement par le navigateur. +Il permet de stocker des données directement chez le client. +Il s'agit donc d'un outil extrêmement puissant, qui permet d'éviter +beaucoup de requêtes au serveur, améliorant ainsi les temps de chargement. + +Cependant, il y a deux limitations majeures à prendre en compte : + +- le `localStorage` est entièrement géré par le client, + une fois le déploiement effectué, vous ne pouvez plus y toucher ; + vous devez donc être sûr de vous avant d'apporter des modifications + reposant sur le `localStorage`. +- la quantité de données stockable est limitée à 10Mo ; + une fois ce quota rempli, le navigateur lèvera une `QuotaExceededError`. + +## Invalidation du `localStorage` + +Pour résoudre le premier de ces deux problèmes, il y a un script permettant +d'annuler une partie du cache. +Ce dernier se trouve dans le fichier `core/static/bundled/core/cache.ts`. + +Vous devrez modifier ce cache chaque fois que vous effectuerez +un changement de schéma, c'est-à-dire dans un des cas suivants : + +- une des clefs du cache n'est plus utilisée +- la clef n'a pas changé, mais la manière dont les données attachées à cette clef + sont formées a été modifiée. + +!!!Note + + Si vous ne faites qu'ajouter des données, sans modifier ni supprimer + celles qui sont là, vous n'avez pas besoin d'invalider le cache. + +Vous devez effectuer deux modifications dans ce fichier : + +- incrémenter la version du cache +- ajouter une ligne permettant de retirer votre clef du cache + +```ts hl_lines="2 11" +// increment this number when a breaking change is made with localStorage +const CURRENT_CACHE_VERSION = 2; // <-- changez cette ligne + +export function cacheBuster() { + const version = Number.parseInt(localStorage.getItem("version") ?? "0", 10); + if (version === CURRENT_CACHE_VERSION) { + // The cache schema is up-to-date. Nothing to do. + return; + } + // ... + localStorage.removeItem(""); // <-- et rajoutez cette ligne + localStorage.setItem("version", CURRENT_CACHE_VERSION.toString()); +} +``` diff --git a/eboutic/static/bundled/eboutic/eboutic-index.ts b/eboutic/static/bundled/eboutic/eboutic-index.ts index 43080a58..5421d428 100644 --- a/eboutic/static/bundled/eboutic/eboutic-index.ts +++ b/eboutic/static/bundled/eboutic/eboutic-index.ts @@ -7,8 +7,7 @@ interface BasketItem { unitPrice: number; } -// increment the key number if the data schema of the cached basket changes -const BASKET_CACHE_KEY = "basket1"; +const BASKET_CACHE_KEY = "basket"; document.addEventListener("alpine:init", () => { Alpine.data("basket", (lastPurchaseTime?: number) => ({ diff --git a/mkdocs.yml b/mkdocs.yml index f537fa76..cd6df0de 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,6 +66,8 @@ nav: - Gestion des permissions: tutorial/perms.md - Gestion des groupes: tutorial/groups.md - Les fragments: tutorial/fragments.md + - Frontend: + - localStorage: tutorial/front/localstorage.md - API: - Développement: tutorial/api/dev.md - Connexion à l'API: tutorial/api/connect.md