Skip to content

Commit de69bc9

Browse files
Internal: Service worker: disable offline storage and avoid intercepting HTML/API routes
1 parent 915d0fc commit de69bc9

1 file changed

Lines changed: 34 additions & 54 deletions

File tree

public/service-worker.js

Lines changed: 34 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,38 @@
11
/**
22
* Chamilo Service Worker
33
* Handles:
4-
* - Offline caching
54
* - Push notifications
5+
* - (Optional) Network fallback response when offline
66
*
77
* Notes:
8-
* - We avoid caching or intercepting dynamic endpoints (/api, /themes) to prevent noisy console logs
8+
* - We avoid intercepting dynamic endpoints (/api, /themes) to prevent noisy console logs
99
* when responses are 404 (expected in strict checks) or 202 (async endpoints).
10-
* - We only cache successful GET responses (res.ok).
10+
* - We do NOT store responses. This prevents stale navigation/session issues.
1111
*/
1212

13-
const CACHE_NAME = "chamilo-cache-v1"
1413
const PRECACHE_URLS = [
1514
"/",
1615
"/manifest.json",
1716
"/img/pwa-icons/icon-192.png",
1817
"/img/pwa-icons/icon-512.png",
1918
]
2019

21-
// PWA: Cache basic files on install
20+
// PWA: Keep install/activate flow (no persistent storage)
2221
self.addEventListener("install", (event) => {
2322
console.log("[Service Worker] Install event")
2423

24+
// Validate basic URLs at install time to fail fast if something is missing.
25+
// We intentionally do not store anything.
2526
event.waitUntil(
26-
caches.open(CACHE_NAME).then((cache) => {
27-
return cache.addAll(PRECACHE_URLS)
28-
})
27+
Promise.all(
28+
PRECACHE_URLS.map(async (url) => {
29+
try {
30+
await fetch(url, { cache: "no-store" })
31+
} catch (e) {
32+
// Best-effort: do not block SW install if a non-critical asset fails
33+
}
34+
})
35+
)
2936
)
3037

3138
// Activate updated SW ASAP
@@ -35,22 +42,7 @@ self.addEventListener("install", (event) => {
3542
self.addEventListener("activate", (event) => {
3643
console.log("[Service Worker] Activate event")
3744

38-
event.waitUntil(
39-
(async () => {
40-
// Cleanup old caches if cache name changes in the future
41-
const keys = await caches.keys()
42-
await Promise.all(
43-
keys.map((key) => {
44-
if (key !== CACHE_NAME) {
45-
return caches.delete(key)
46-
}
47-
return Promise.resolve()
48-
})
49-
)
50-
51-
await self.clients.claim()
52-
})()
53-
)
45+
event.waitUntil(self.clients.claim())
5446
})
5547

5648
/**
@@ -60,28 +52,32 @@ self.addEventListener("activate", (event) => {
6052
function shouldBypass(request) {
6153
const url = new URL(request.url)
6254

55+
// Do not handle non-GET requests
56+
if (request.method !== "GET") return true
57+
6358
// Ignore internal redirect helper routes
6459
if (url.pathname.startsWith("/r/")) return true
6560

66-
// Avoid intercepting theme assets (prevents console noise for strict 404 checks)
61+
// Avoid intercepting theme assets/endpoints
6762
if (url.pathname.startsWith("/themes/")) return true
6863

69-
// Avoid intercepting API calls (can return 202 or other statuses that shouldn't be cached)
64+
// Avoid intercepting API calls
7065
if (url.pathname.startsWith("/api/")) return true
7166

72-
// Avoid strict probing calls anywhere (expected 404 should not produce SW noise)
67+
// Avoid strict probing calls anywhere
7368
if (url.searchParams.has("strict")) return true
7469

75-
// Do not handle non-GET requests
76-
if (request.method !== "GET") return true
70+
// Never intercept HTML navigations/documents (prevents any navigation impact)
71+
if (request.mode === "navigate" || request.destination === "document") return true
72+
const accept = request.headers.get("accept") || ""
73+
if (accept.includes("text/html")) return true
7774

7875
return false
7976
}
8077

8178
/**
82-
* Cache-first for static navigations/assets, network fallback.
83-
* - Only caches successful responses (res.ok).
84-
* - If network fails, returns cache if available.
79+
* Network-first (no storage), with graceful offline responses.
80+
* This avoids stale resources while still providing a friendly offline fallback.
8581
*/
8682
self.addEventListener("fetch", (event) => {
8783
const request = event.request
@@ -94,37 +90,21 @@ self.addEventListener("fetch", (event) => {
9490
event.respondWith(
9591
(async () => {
9692
try {
97-
const cachedResponse = await caches.match(request)
98-
if (cachedResponse) {
99-
return cachedResponse
100-
}
101-
102-
const response = await fetch(request)
103-
104-
// Cache only successful responses
105-
if (response && response.ok) {
106-
const cache = await caches.open(CACHE_NAME)
107-
await cache.put(request, response.clone())
108-
}
109-
110-
return response
93+
// Always go to network (no SW storage)
94+
return await fetch(request)
11195
} catch (err) {
112-
// Network failure: try cache
113-
const cachedResponse = await caches.match(request)
114-
if (cachedResponse) {
115-
return cachedResponse
116-
}
117-
118-
// As last resort, provide a friendly offline response for navigations
96+
// Offline fallback
11997
const accept = request.headers.get("accept") || ""
98+
99+
// If someone requested HTML (rare here because we bypass HTML), return a friendly page anyway
120100
if (accept.includes("text/html")) {
121101
return new Response(
122102
"<h1>Offline</h1><p>The application is currently offline.</p>",
123103
{ headers: { "Content-Type": "text/html; charset=utf-8" }, status: 503 }
124104
)
125105
}
126106

127-
// For other assets, just fail gracefully
107+
// For assets, fail gracefully
128108
return new Response("Offline", { status: 503, statusText: "Service Unavailable" })
129109
}
130110
})()

0 commit comments

Comments
 (0)