Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Steur <tsteur@users.noreply.github.com>2020-10-02 03:20:20 +0300
committerGitHub <noreply@github.com>2020-10-02 03:20:20 +0300
commit2807e94baaa9b545f27a55a7dd071ba187799491 (patch)
tree9fa5722fe414e7f1e664d4226b2620d215fd1987 /offline-service-worker.js
parent06d43857c48ada2fa7f1ad18a8309e8826c0e413 (diff)
JS Offline tracking (#15970)
* JS Offline tracking * minor tweaks * add some tests * add some tests * apply review feedback
Diffstat (limited to 'offline-service-worker.js')
-rw-r--r--offline-service-worker.js175
1 files changed, 175 insertions, 0 deletions
diff --git a/offline-service-worker.js b/offline-service-worker.js
new file mode 100644
index 0000000000..a00fe1a906
--- /dev/null
+++ b/offline-service-worker.js
@@ -0,0 +1,175 @@
+var matomoAnalytics = {initialize: function (options) {
+ if ('object' !== typeof options) {
+ options = {};
+ }
+
+ var maxLimitQueue = options.queueLimit || 50;
+ var maxTimeLimit = options.timeLimit || (60 * 60 * 24); // in seconds...
+ // same as configured in in tracking_requests_require_authentication_when_custom_timestamp_newer_than
+
+ function getQueue()
+ {
+ return new Promise(function(resolve, reject) {
+ // do a thing, possibly async, then...
+
+ if (!indexedDB) {
+ reject(new Error('No support for IndexedDB'));
+ return;
+ }
+ var request = indexedDB.open("matomo", 1);
+
+ request.onerror = function() {
+ console.error("Error", request.error);
+ reject(new Error(request.error));
+ };
+ request.onupgradeneeded = function(event) {
+ console.log('onupgradeneeded')
+ var db = event.target.result;
+
+ if (!db.objectStoreNames.contains('requests')) {
+ db.createObjectStore('requests', {autoIncrement : true, keyPath: 'id'});
+ }
+
+ };
+ request.onsuccess = function(event) {
+ var db = event.target.result;
+ let transaction = db.transaction("requests", "readwrite");
+ let requests =transaction.objectStore("requests");
+ resolve(requests);
+
+
+ };
+ });
+ }
+
+ function syncQueue () {
+ // check something in indexdb
+ return getQueue().then(function (queue) {
+ queue.openCursor().onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (cursor && navigator.onLine) {
+ cursor.continue();
+ var queueId = cursor.value.id;
+
+ var secondsQueuedAgo = ((Date.now() - cursor.value.created) / 1000);
+ secondsQueuedAgo = parseInt(secondsQueuedAgo, 10);
+ if (secondsQueuedAgo > maxTimeLimit) {
+ // too old
+ getQueue().then(function (queue) {
+ queue.delete(queueId);
+ });
+ return;
+ }
+
+ console.log("Cursor " + cursor.key);
+
+ var init = {
+ headers: cursor.value.headers,
+ method: cursor.value.method,
+ }
+ if (cursor.value.body) {
+ init.body = cursor.value.body;
+ }
+
+ if (cursor.value.url.includes('?')) {
+ cursor.value.url += '&cdo=' + secondsQueuedAgo;
+ } else if (init.body) {
+ // todo test if this actually works for bulk requests
+ init.body = init.body.replace('&idsite=', '&cdo=' + secondsQueuedAgo + '&idsite=');
+ }
+
+ fetch(cursor.value.url, init).then(function (response) {
+ console.log('server response', response);
+ if (response.status < 400) {
+ getQueue().then(function (queue) {
+ queue.delete(queueId);
+ });
+ }
+ }).catch(function (error) {
+ console.error('Send to Server failed:', error);
+ throw error
+ })
+ }
+ else {
+ console.log("No more entries!");
+ }
+ };
+ });
+ }
+
+ function limitQueueIfNeeded(queue)
+ {
+ var countRequest = queue.count();
+ countRequest.onsuccess = function(event) {
+ if (event.result > maxLimitQueue) {
+ // we delete only one at a time because of concurrency some other process might delete data too
+ queue.openCursor().onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (cursor) {
+ queue.delete(cursor.value.id);
+ limitQueueIfNeeded(queue);
+ }
+ }
+ }
+ }
+ }
+
+ self.addEventListener('sync', function(event) {
+ if (event.tag === 'matomoSync') {
+ syncQueue();
+ }
+ });
+
+ self.addEventListener('fetch', function (event) {
+ let isOnline = navigator.onLine;
+
+ let isTrackingRequest = (event.request.url.includes('/matomo.php')
+ || event.request.url.includes('/piwik.php'));
+ let isTrackerRequest = event.request.url.endsWith('/matomo.js')
+ || event.request.url.endsWith('/piwik.js');
+
+ if (isTrackerRequest) {
+ if (isOnline) {
+ syncQueue();
+ }
+ caches.open('matomo').then(function(cache) {
+ return cache.match(event.request).then(function (response) {
+ return response || fetch(event.request).then(function(response) {
+ cache.put(event.request, response.clone());
+ return response;
+ });
+ });
+ })
+ } else if (isTrackingRequest && isOnline) {
+ syncQueue();
+ event.respondWith(fetch(event.request));
+ } else if (isTrackingRequest && !isOnline) {
+
+ var headers = {};
+ for (const [header, value] of event.request.headers) {
+ headers[header] = value;
+ }
+
+ let requestInfo = {
+ url: event.request.url,
+ referrer : event.request.referrer,
+ method : event.request.method,
+ referrerPolicy : event.request.referrerPolicy,
+ headers : headers,
+ created: Date.now()
+ };
+ event.request.text().then(function (postData) {
+ requestInfo.body = postData;
+
+ getQueue().then(function (queue) {
+ queue.add(requestInfo);
+ limitQueueIfNeeded(queue);
+
+ return queue;
+ });
+ });
+
+ }
+ });
+}
+}; \ No newline at end of file