-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathservice-worker.js.php
180 lines (138 loc) · 4.9 KB
/
service-worker.js.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* <?php
$cacheFiles = json_decode(file_get_contents(__DIR__.'/cache.json'), true)['files'];
$fileVersions = [];
foreach ($cacheFiles as $file) {
$fileVersions[$file] = version([$file]);
}
$maxVersion = max($fileVersions);
?> */
importScripts('./ext/localforage.min.js');
const dataStorage = localforage.createInstance({
name: 'solaire',
storeName: 'data',
driver: localforage.INDEXEDDB
});
const cachePrefix = 'solaire-sw';
const currentCacheName = `${cachePrefix}-<?=$maxVersion?>`;
const liveFileVersions = JSON.parse(`<?=json_encode($fileVersions, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)?>`);
///// EVENTS
// INSTALL
self.addEventListener('install', function(event) {
console.log('[install] Installing service worker...');
event.waitUntil(
installFiles(event)
.catch(raison => console.log('[install] ' + raison))
.then(() => {
console.log('[install] Service worker installed!');
return true;
})
.catch(error => console.error(error))
)
});
// ACTIVATE
self.addEventListener('activate', function(event) {
console.log('[activate] Activating service worker...');
event.waitUntil(self.clients.claim()); // makes the service worker take control of all clients
console.log('[activate] Service worker active!');
});
// FETCH
self.addEventListener('fetch', function(event) {
const ignore = ['find.php', 'service-worker.js.php', 'backend/update.php'];
if (event.request.url.match(ignore.join('|'))) return;
const fetchIndex = event.request.url.match('solaire/systeme/');
event.respondWith(
caches.open(currentCacheName)
.then(cache => {
const request = fetchIndex ? './' : event.request;
return cache.match(request)
})
.then(matching => {
return matching || fetch(event.request);
})
.catch(error => console.error(error))
)
});
// MESSAGE
self.addEventListener('message', async function(event) {
console.log('[sw] Message reçu :', event.data);
const action = event.data?.action;
switch (action) {
case 'force-update': {
const source = event.ports[0];
try {
await self.skipWaiting();
source.postMessage({ ready: true });
} catch (error) {
source.postMessage({ ready: false, error });
}
}
}
});
///// FUNCTIONS
/**
* Installs all app files.
* @param {Event|null} event - The event that caused the file installation.
*/
async function installFiles(event = null) {
const localFileVersions = (await dataStorage.getItem('file-versions')) ?? {};
const filesQuantity = Object.keys(liveFileVersions).length;
const newCache = await caches.open(currentCacheName);
const source = event?.source || null;
const action = event?.data?.action || 'install';
try {
console.log(`[${action}] Installing files...`);
await Promise.all(Object.keys(liveFileVersions).map(async file => {
const oldVersion = localFileVersions[file] ?? 0;
const newVersion = liveFileVersions[file];
let response;
const request = new Request(file, { cache: 'no-store' });
// If the online file is more recent than the locally installed file
if (newVersion > oldVersion || oldVersion === 0) {
response = await fetch(request);
console.log(`File ${file} updated (${oldVersion} => ${newVersion})`);
}
// If the locally installed file is already the most recent version
else {
response = (await caches.match(new Request(file))) ?? (await fetch(request));
}
if (!response.ok) throw Error(`[${action}] File download failed: (${request.url})`);
await newCache.put(request, response);
if (source) source.postMessage({ action: 'update-files', loaded: true, total: filesQuantity, url: file });
return;
}));
console.log(`[${action}] File installation complete!`);
deleteOldCaches(currentCacheName, action);
dataStorage.setItem('file-versions', liveFileVersions);
}
// If there was an error while installing the new files, delete them
// and keep the old cache.
catch (error) {
await caches.delete(newCache);
console.log(`[${action}] File installation cancelled.`);
throw error;
}
return;
}
/**
* Deletes old caches.
* @param {string} newCacheName - The new cache's name.
* @param {'install'|'update'} action - The action during which the caches were removed.
*/
async function deleteOldCaches(newCacheName, action) {
try {
const allCaches = await caches.keys();
if (allCaches.length <= 1) throw 'no-cache';
console.log(`[${action}] Deleting old cached files`);
await Promise.all(
allCaches.map(cache => {
if (cache.startsWith(cachePrefix) && newCacheName != cache) return caches.delete(cache);
else return Promise.resolve();
})
);
console.log(`[${action}] Cache cleanup complete!`);
} catch (error) {
if (error === 'no-cache') console.log('No old cache to delete');
else console.error(error);
}
return;
}