Evict stale precache service worker from old Agora deployment
A previous version of Agora deployed at agora.spot shipped a precaching service worker that is still controlling returning browsers and serving them stale HTML/JS — they never see new deploys. The fix has three parts: 1. public/sw.js — on activate, delete every Cache Storage entry the old SW left behind. This SW has no fetch handler, so once it takes over nothing re-populates the cache. 2. src/main.tsx — register /sw.js unconditionally on every web page load. Previously only usePushNotifications registered it, which meant users who never visited NotificationSettings stayed pinned to the old SW forever. Native (Capacitor) skips this — there is no stale SW on the filesystem origin. 3. .gitlab-ci.yml — the deploy-web rsync was excluding sw.js from the first pass and never re-adding it to the second pass, so deploys silently never updated sw.js. Now it ships in the second pass alongside index.html (after hashed assets land).
This commit is contained in:
+7
-5
@@ -49,12 +49,14 @@ deploy-web:
|
||||
- echo "$DEPLOY_SSH_KEY" | tr -d '\r' > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
|
||||
- ssh-keyscan -H "${DEPLOY_TARGET##*@}" >> ~/.ssh/known_hosts 2>/dev/null
|
||||
|
||||
# Two-phase rsync: upload hashed assets first, then index.html, so the
|
||||
# site never serves an index.html that points at assets that haven't
|
||||
# finished uploading. The destination ":/" is the rrsync jail root on
|
||||
# venus, which maps to /var/www/agora.spot/.
|
||||
# Two-phase rsync: upload hashed assets first, then index.html and sw.js,
|
||||
# so the site never serves an index.html that points at assets that
|
||||
# haven't finished uploading. sw.js is in the second pass for the same
|
||||
# reason — it's a stable filename that all browsers re-fetch to check
|
||||
# for updates, so we want it to land last. The destination ":/" is the
|
||||
# rrsync jail root on venus, which maps to /var/www/agora.spot/.
|
||||
- rsync -av --exclude=/sw.js --exclude=/index.html -e "ssh -i ~/.ssh/id_ed25519" dist/ "${DEPLOY_TARGET}:/"
|
||||
- rsync -av -e "ssh -i ~/.ssh/id_ed25519" dist/index.html "${DEPLOY_TARGET}:/"
|
||||
- rsync -av -e "ssh -i ~/.ssh/id_ed25519" dist/index.html dist/sw.js "${DEPLOY_TARGET}:/"
|
||||
|
||||
# Disabled: nsite deploy not needed right now; re-enable by restoring the
|
||||
# rules below to run on default branch (and ensure NSITE_NBUNKSEC is set).
|
||||
|
||||
+16
-1
@@ -56,8 +56,23 @@ self.addEventListener('notificationclick', (event) => {
|
||||
});
|
||||
|
||||
// --- Activate immediately ---
|
||||
//
|
||||
// On activate, nuke every Cache Storage entry. A previous version of Agora
|
||||
// deployed a precaching service worker (Workbox-style) that's still serving
|
||||
// stale HTML/JS to returning users on the same origin. Wiping caches here
|
||||
// means the first request a returning user makes after this SW takes over
|
||||
// will hit the network and get the new build.
|
||||
//
|
||||
// This SW itself does not intercept fetches (no 'fetch' handler), so it
|
||||
// never repopulates a cache — only push notifications are handled below.
|
||||
|
||||
self.addEventListener('install', () => self.skipWaiting());
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(self.clients.claim());
|
||||
event.waitUntil(
|
||||
(async () => {
|
||||
const keys = await caches.keys();
|
||||
await Promise.all(keys.map((key) => caches.delete(key)));
|
||||
await self.clients.claim();
|
||||
})(),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -86,3 +86,26 @@ createRoot(document.getElementById("root")!).render(
|
||||
requestAnimationFrame(() => {
|
||||
document.getElementById('preloader')?.remove();
|
||||
});
|
||||
|
||||
// ─── Service worker takeover (web only) ──────────────────────────────────────
|
||||
//
|
||||
// A previous version of Agora deployed at this origin shipped a precaching
|
||||
// service worker that's still serving stale HTML/JS to returning users. The
|
||||
// SW we ship now (public/sw.js) has no fetch handler and nukes every cache
|
||||
// on activate, so as soon as the browser installs it, returning users start
|
||||
// getting fresh builds again.
|
||||
//
|
||||
// usePushNotifications() also registers /sw.js, but only when the user
|
||||
// visits the notification settings page — that's not good enough to evict
|
||||
// the old SW for everyone. We register here on every web page load so the
|
||||
// new SW takes over for all visitors, regardless of whether they use push.
|
||||
//
|
||||
// Native (Capacitor) skips this — the bundled web assets are served from
|
||||
// the local filesystem and there's no stale SW on the origin to evict.
|
||||
if (!Capacitor.isNativePlatform() && 'serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/sw.js', { scope: '/' }).catch((err) => {
|
||||
console.warn('[sw] registration failed:', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user