fix(pwa): BUG-005 chargement mobile via Cloudflare (SW network-first + no-cache)
CI / lint (push) Successful in 58s
CI / security (push) Successful in 40s
CI / test (push) Successful in 1m19s
CI / build (push) Successful in 37s
CI / e2e (push) Successful in 10m53s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s

This commit is contained in:
2026-09-11 17:13:16 -04:00
parent 4c4e415975
commit 7cf7d33b6d
10 changed files with 382 additions and 153 deletions
+2
View File
@@ -114,6 +114,7 @@ Avant de corriger quoi que ce soit, un agent IA doit :
| | | | | | | | | | |
| *BUG-003* | `mypy` : 33 erreurs de typage (étape CI en mode advisory → bloquante) | 🟢 corrigé | P2 | ⚙️ backend | IA | `backend/{main,indexer,export,pdf_reader,bookslm_routes}.py`, `backend/auth/router.py`, `.gitea/workflows/ci.yml` | `mypy backend/ --ignore-missing-imports` | Annotations de types, gardes `None` (auth/router), import `PROVIDERS` manquant (bug latent `main.py:4523`), `PdfReader: Any` ; CI mypy rendu bloquant | 0 erreur après correction. `PROVIDERS` non importé → `NameError` avalé par le `except` (le modèle par défaut n'était jamais prépendé). Tests : 728 passed |
| *BUG-004* | Lien cassé vers `CONTRIBUTING.md` dans `README.md` | 🟢 corrigé | P3 | 📄 docs | IA | `README.md` | Cliquer le lien « Contributing » | `README.md` : lien → `docs/CONTRIBUTING.md` (+ `DELIVERY_WORKFLOW.md`) ; arbre du projet corrigé | `README.fr.md` pointait déjà correctement vers `./docs/CONTRIBUTING.md` |
| *BUG-005* | Sur mobile via Cloudflare (`og.dracodev.net`), le site ne charge pas complètement même après purge des caches (téléphone + Cloudflare) | 🟢 corrigé | P1 | 📱 frontend + ⚙️ backend | IA | `frontend/sw.js`, `frontend/index.html`, `frontend/js/sync.js`, `backend/main.py` | Ouvrir `og.dracodev.net` sur mobile (PWA installée), purger les caches, recharger | SW **network-first** pour le code + caches versionnés (`SW_VERSION`) ; précache corrigé (`/static/js/…`) ; migration ponctuelle `localStorage` ; `Cache-Control: no-cache` (le middleware renvoyait `immutable` 1 an sur `/static`) ; tests `tests/frontend/sw.test.mjs` + `TestStaticCaching` | Cause : SW cache-first à nom fixe + en-têtes `immutable` non fingerprinted → ancien build servi indéfiniment (Cache Storage ≠ cache navigateur/CF). Vérifié : 754 tests backend, frontend OK |
### TODOs techniques (améliorations / nouvelles tâches)
@@ -135,6 +136,7 @@ Avant de corriger quoi que ce soit, un agent IA doit :
| *(exemple)* 2026-06-15 | BUG-001 | Correction | `frontend/app.js` | Réécriture de `renderFile()` pour préserver le DOM dashboard | 🟢 corrigé (en attente vérif) |
| 2026-09-09 | BUG-001, BUG-002 | Correction | `backend/main.py`, `frontend/excalidraw-editor.html`, `tests/test_pdf_stream.py` | BUG-001: Content-Disposition RFC 5987 (nom PDF accentué ne casse plus l'en-tête → plus de 500). BUG-002: suppression alias esm.sh (408 jotai) + React 19 cohérent + prop `excalidrawAPI` → Loading masqué, save OK. Vérifié: 534 tests backend verts + E2E navigateur. | 🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-11 | BUG-003, BUG-004 | Correction | `backend/{main,indexer,export,pdf_reader,bookslm_routes}.py`, `backend/auth/router.py`, `.gitea/workflows/ci.yml`, `README.md`, `README.fr.md` | BUG-003: 33 erreurs mypy corrigées (annotations, gardes `None`, import `PROVIDERS` manquant → bug latent) + étape CI mypy rendue bloquante. BUG-004: lien `README.md` → `docs/CONTRIBUTING.md`. Vérifié: mypy 0 erreur, ruff OK, pytest 728 passed, frontend OK. | 🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-11 | BUG-005 | Correction | `frontend/sw.js`, `frontend/index.html`, `frontend/js/sync.js`, `backend/main.py`, `.gitea/workflows/ci.yml`, `tests/frontend/sw.test.mjs`, `tests/test_api_main.py` | BUG-005: chargement mobile incomplet via Cloudflare. SW réécrit en **network-first** pour HTML/JS/CSS + caches versionnés ; précache corrigé (`/static/js/…`) ; kill-switch de session remplacé par une migration `localStorage` ponctuelle ; en-têtes `/static` passés de `immutable 1 an` à `no-cache` ; `index.html`/`manifest`/SPA en `no-cache` ; reload unique sur `controllerchange`. Vérifié: 754 tests backend, frontend 9+28+8 OK, ruff/mypy OK. | 🟢 corrigé (en attente vérif utilisateur) |
---
+47 -6
View File
@@ -94,11 +94,14 @@ Définit les métadonnées de l'application :
### Service Worker (sw.js)
Gère le cache et le mode hors ligne :
- **Cache statique** : Interface et assets
- **Cache dynamique** : Données API (max 50 entrées)
- **Stratégies** : Cache-first et Network-first
- **Nettoyage** : Suppression automatique des anciens caches
Gère le cache et le mode hors ligne (version `SW_VERSION`) :
- **Code (HTML/JS/CSS/manifest)** : **network-first** (cache en secours hors-ligne)
- **API** : network-first (+ cache hors-ligne)
- **Autres assets (images, polices)** : stale-while-revalidate
- **Nettoyage** : purge automatique des caches d'une version antérieure à l'activation
> Le choix network-first est délibéré : les assets ne sont pas fingerprintés, un
> cache-first servait donc un ancien build indéfiniment sur mobile (BUG-005).
### Icônes PWA
@@ -271,7 +274,13 @@ location /sw.js {
}
location /manifest.json {
add_header Cache-Control "public, max-age=3600";
add_header Cache-Control "no-cache";
proxy_pass http://obsigate:8080;
}
# Les assets /static ne sont PAS fingerprintés : forcer la revalidation.
location /static/ {
add_header Cache-Control "no-cache";
proxy_pass http://obsigate:8080;
}
```
@@ -287,6 +296,38 @@ http:
Service-Worker-Allowed: "/"
```
### Cloudflare (BUG-005)
Derrière Cloudflare, un **service worker cache-first** combiné à un en-tête
`Cache-Control: immutable` sur des assets non fingerprintés peut servir un
ancien build indéfiniment — même après « Purge Everything » : le **Cache Storage
du navigateur (Service Worker) est un stockage séparé** que la purge Cloudflare
et le « vider le cache » du téléphone ne touchent pas.
Correctifs appliqués côté code (ObsiGate ≥ 2.2.1) :
- SW **network-first** pour HTML/JS/CSS (cache seulement en secours hors-ligne),
caches versionnés (`SW_VERSION`) purgés à l'activation.
- `Cache-Control: no-cache` sur `/static`, `index.html`, `manifest.json` et les
pages HTML (revalidation ETag, pas de cache long).
- Migration ponctuelle `localStorage` qui supprime les anciens caches sur les
appareils bloqués.
Réglages Cloudflare recommandés (dashboard → zone `og.dracodev.net`) :
| Réglage | Valeur | Pourquoi |
|---|---|---|
| **Rocket Loader** | **Désactivé** | Réécrit/defer les scripts et casse les `type="module"`. |
| **Auto Minify (JS/CSS)** | **Désactivé** | Peut casser la syntaxe JS moderne. |
| **Cache Rules / Page Rules** | Pas de « Cache Everything » sur HTML | Sinon Cloudflare ignore l'en-tête `no-cache`. |
| **Browser Cache TTL** | « Respect Existing Headers » | Laisse ObsiGate décider (`no-cache`). |
| **WebSockets** | Activé | Nécessaire aux connexions temps réel (SSE/WS). |
| **Purge Cache** | Après chaque déploiement | Évite un mix ancien/nouveau au premier accès. |
> Si un appareil reste bloqué malgré tout : sur mobile, désinstaller la PWA
> (ou « Site settings → Clear & reset ») puis rouvrir. La migration
> `obsigate-sw-migration` se charge normalement du nettoyage automatiquement.
## 🔒 Sécurité
### Content Security Policy