More web push progress.

Ylian Saint-Hilaire committed Feb 9, 2021 at 15:31 UTC af1fcb84311b8edb60b98e2cf085f2ff9bb7f3b6
4 files changed +86 -22
meshuser.js
+27
@@ -2746,6 +2746,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2746 if (parent.parent.multiServer != null) {
2747 // TODO: Add multi-server support
2748 }
2749 +
2750 + // If the user is not connected, use web push if available.
2751 + if ((parent.wssessions[chguser._id] == null) && (parent.sessionsCount[chguser._id] == null)) {
2752 + // Perform web push notification
2753 + var payload = { body: command.msg, icon: 8 }; // Icon 8 is the user icon.
2754 + if (command.url) { payload.url = command.url; }
2755 + if (domain.title != null) { payload.title = domain.title; } else { payload.title = "MeshCentral"; }
2756 + payload.title += ' - ' + user.name;
2757 + parent.performWebPush(domain, chguser, payload, { TTL: 60 }); // For now, 1 minute TTL
2758 + }
2759 +
2760 break;
2761 }
2762 case 'meshmessenger':
@@ -2772,6 +2783,22 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
2783 if (parent.parent.multiServer != null) {
2784 // TODO: Add multi-server support
2785 }
2786 +
2787 + // If the user is not connected, use web push if available.
2788 + if ((parent.wssessions[chguser._id] == null) && (parent.sessionsCount[chguser._id] == null)) {
2789 + // Create the server url
2790 + var httpsPort = ((args.aliasport == null) ? args.port : args.aliasport); // Use HTTPS alias port is specified
2791 + var xdomain = (domain.dns == null) ? domain.id : '';
2792 + if (xdomain != '') xdomain += "/";
2793 + var url = "https://" + parent.getWebServerName(domain) + ":" + httpsPort + "/" + xdomain + "messenger?id=meshmessenger/" + encodeURIComponent(command.userid) + "/" + encodeURIComponent(user._id);
2794 +
2795 + // Perform web push notification
2796 + var payload = { body: "Chat Request, Click here to accept.", icon: 8, url: url }; // Icon 8 is the user icon.
2797 + if (domain.title != null) { payload.title = domain.title; } else { payload.title = "MeshCentral"; }
2798 + payload.title += ' - ' + user.name;
2799 + parent.performWebPush(domain, chguser, payload, { TTL: 60 }); // For now, 1 minute TTL
2800 + }
2801 + return;
2802 }
2803
2804 // User-to-device chat is not support in LAN-only mode yet. We need the agent to replace the IP address of the server??
public/serviceworker.js
+9 -14
@@ -1,18 +1,13 @@
1
2 self.addEventListener('push', function (event) {
3 - console.log('Service Worker push', JSON.stringify(event));
4 - if (event.data) {
5 - console.log("Push event!! ", event.data.text());
6 - showLocalNotification("Yolo", event.data.text(), self.registration);
7 - } else {
8 - console.log("Push event but no data");
9 - }
3 + if (event.data == null) return;
4 + var json = event.data.json();
5 + const options = { body: json.body, icon: '/favicon-303x303.png', tag: json.url };
6 + if (json.icon) { options.icon = '/images/notify/icons128-' + json.icon + '.png'; }
7 + self.registration.showNotification(json.title, options);
8 });
9
12 -const showLocalNotification = function(title, body, swRegistration) {
13 - const options = {
14 - body
15 - // here you can add more properties like icon, image, vibrate, etc.
16 - };
17 - swRegistration.showNotification(title, options);
18 -};
\ No newline at end of file
10 +self.addEventListener('notificationclick', function (event) {
11 + event.notification.close();
12 + if ((event.notification.tag != null) && (event.notification.tag != '')) { event.waitUntil(self.clients.openWindow(event.notification.tag)); }
13 +});
\ No newline at end of file
views/default.handlebars
+8 -8
@@ -12732,10 +12732,10 @@
12732 x += '<input type=button value="' + "Notes" + '" title="' + "View notes about this user" + '" onclick=showNotes(false,"' + encodeURIComponentEx(user._id) + '") />';
12733 if (user.phone && (features & 0x02000000)) { x += '<input type=button value="' + "SMS" + '" title="' + "Send a SMS message to this user" + '" onclick=showSendSMS("' + encodeURIComponentEx(user._id) + '") />'; }
12734 if ((typeof user.email == 'string') && (user.emailVerified === true) && (features & 0x00000040)) { x += '<input type=button value="' + "Email" + '" title="' + "Send a email message to this user" + '" onclick=showSendEmail("' + encodeURIComponentEx(user._id) + '") />'; }
12735 - if (!self && (activeSessions > 0)) {
12735 + if (!self && ((activeSessions > 0) || ((features2 & 8) && (user.webpush)))) {
12736 x += '<input type=button value="' + "Notify" + '" title="' + "Send user notification" + '" onclick=showUserAlertDialog(event,"' + encodeURIComponentEx(user._id) + '") />';
12737 x += '<input type=button value="' + "Chat" + '" title="' + "Chat" + '" onclick=userChat(event,"' + encodeURIComponentEx(user._id) + '","' + encodeURIComponentEx(user.name) + '") />';
12738 - if ((serverinfo != null) && (serverinfo.altmessenging != null)) { for (var i in serverinfo.altmessenging) { x += '<input type=button value="' + EscapeHtml(serverinfo.altmessenging[i].name) + '" onclick=altUserChat(event,"' + encodeURIComponentEx(user._id) + '","' + encodeURIComponentEx(user.name) + '",' + i + ') />'; } }
12738 + if ((activeSessions > 0) && (serverinfo != null) && (serverinfo.altmessenging != null)) { for (var i in serverinfo.altmessenging) { x += '<input type=button value="' + EscapeHtml(serverinfo.altmessenging[i].name) + '" onclick=altUserChat(event,"' + encodeURIComponentEx(user._id) + '","' + encodeURIComponentEx(user.name) + '",' + i + ') />'; } }
12739 }
12740
12741 // Setup the panel
@@ -13852,11 +13852,11 @@
13852 navigator.serviceWorker.ready.then(function(reg) {
13853 reg.pushManager.subscribe({ applicationServerKey: urlBase64ToUint8Array(serverinfo.vapidpublickey), userVisibleOnly: true }).then(function(sub) {
13854 meshserver.send({ action: 'webpush', sub: sub });
13855 - }).catch(function(e) { console.error('Unable to subscribe to push', e); });
13855 + }).catch(function(e) { console.error('Worker: Unable to subscribe to push', e); });
13856 })
13857 }).catch(function(error) {
13858 // Registration failed
13859 - console.log('Registration failed', error);
13859 + console.log('Worker: Registration failed', error);
13860 });
13861 }
13862 }
@@ -14405,10 +14405,10 @@
14405
14406 // Used to convert Base64 public VAPID key to bytearray.
14407 function urlBase64ToUint8Array(base64String) {
14408 - const padding = '='.repeat((4 - base64String.length % 4) % 4);
14409 - const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
14410 - const rawData = atob(base64);
14411 - const outputArray = new Uint8Array(rawData.length);
14408 + var padding = '='.repeat((4 - base64String.length % 4) % 4);
14409 + var base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
14410 + var rawData = atob(base64);
14411 + var outputArray = new Uint8Array(rawData.length);
14412 for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); }
14413 return outputArray;
14414 }
webserver.js
+42
@@ -6686,6 +6686,48 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
6686 }
6687 }
6688
6689 + // Perform a web push to a user
6690 + // If any of the push fail, remove the subscription from the user's webpush subscription list.
6691 + obj.performWebPush = function (domain, user, payload, options) {
6692 + if ((parent.webpush == null) || (Array.isArray(user.webpush) == false) || (user.webpush.length == 0)) return;
6693 +
6694 + var completionFunc = function pushCompletionFunc(sub, fail) {
6695 + pushCompletionFunc.failCount += fail;
6696 + if (--pushCompletionFunc.pushCount == 0) {
6697 + if (pushCompletionFunc.failCount > 0) {
6698 + var user = pushCompletionFunc.user, newwebpush = [];
6699 + for (var i in user.webpush) { if (user.webpush[i].fail == null) { newwebpush.push(user.webpush[i]); } }
6700 + user.webpush = newwebpush;
6701 +
6702 + // Update the database
6703 + obj.db.SetUser(user);
6704 +
6705 + // Event the change
6706 + var message = { etype: 'user', userid: user._id, username: user.name, account: obj.CloneSafeUser(user), action: 'accountchange', domain: domain.id, nolog: 1 };
6707 + if (db.changeStream) { message.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come.
6708 + var targets = ['*', 'server-users', user._id];
6709 + if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
6710 + parent.DispatchEvent(targets, obj, message);
6711 + }
6712 + }
6713 + }
6714 + completionFunc.pushCount = user.webpush.length;
6715 + completionFunc.user = user;
6716 + completionFunc.domain = domain;
6717 + completionFunc.failCount = 0;
6718 +
6719 + for (var i in user.webpush) {
6720 + var errorFunc = function pushErrorFunc(error) { pushErrorFunc.sub.fail = 1; pushErrorFunc.call(pushErrorFunc.sub, 1); }
6721 + errorFunc.sub = user.webpush[i];
6722 + errorFunc.call = completionFunc;
6723 + var successFunc = function pushSuccessFunc(value) { pushSuccessFunc.call(pushSuccessFunc.sub, 0); }
6724 + successFunc.sub = user.webpush[i];
6725 + successFunc.call = completionFunc;
6726 + parent.webpush.sendNotification(user.webpush[i], JSON.stringify(payload), options).then(successFunc, errorFunc);
6727 + }
6728 +
6729 + }
6730 +
6731 // Return true if a mobile browser is detected.
6732 // This code comes from "http://detectmobilebrowsers.com/" and was modified, This is free and unencumbered software released into the public domain. For more information, please refer to the http://unlicense.org/
6733 function isMobileBrowser(req) {